diff --git a/includes/libraries/anti-xss-master/src/voku/helper/ASCII.php b/includes/libraries/anti-xss-master/src/voku/helper/ASCII.php new file mode 100644 index 000000000..6688c27ef --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/ASCII.php @@ -0,0 +1,1501 @@ +>|null + */ + private static $ASCII_MAPS; + + /** + * @var array>|null + */ + private static $ASCII_MAPS_AND_EXTRAS; + + /** + * @var array>|null + */ + private static $ASCII_EXTRAS; + + /** + * @var array|null + */ + private static $ORD; + + /** + * @var array|null + */ + private static $LANGUAGE_MAX_KEY; + + /** + * url: https://en.wikipedia.org/wiki/Wikipedia:ASCII#ASCII_printable_characters + * + * @var string + */ + private static $REGEX_ASCII = "[^\x09\x10\x13\x0A\x0D\x20-\x7E]"; + + /** + * bidirectional text chars + * + * url: https://www.w3.org/International/questions/qa-bidi-unicode-controls + * + * @var array + */ + private static $BIDI_UNI_CODE_CONTROLS_TABLE = [ + // LEFT-TO-RIGHT EMBEDDING (use -> dir = "ltr") + 8234 => "\xE2\x80\xAA", + // RIGHT-TO-LEFT EMBEDDING (use -> dir = "rtl") + 8235 => "\xE2\x80\xAB", + // POP DIRECTIONAL FORMATTING // (use -> ) + 8236 => "\xE2\x80\xAC", + // LEFT-TO-RIGHT OVERRIDE // (use -> ) + 8237 => "\xE2\x80\xAD", + // RIGHT-TO-LEFT OVERRIDE // (use -> ) + 8238 => "\xE2\x80\xAE", + // LEFT-TO-RIGHT ISOLATE // (use -> dir = "ltr") + 8294 => "\xE2\x81\xA6", + // RIGHT-TO-LEFT ISOLATE // (use -> dir = "rtl") + 8295 => "\xE2\x81\xA7", + // FIRST STRONG ISOLATE // (use -> dir = "auto") + 8296 => "\xE2\x81\xA8", + // POP DIRECTIONAL ISOLATE + 8297 => "\xE2\x81\xA9", + ]; + + /** + * Get all languages from the constants "ASCII::.*LANGUAGE_CODE". + * + * @return string[] + * + * @phpstan-return array + */ + public static function getAllLanguages(): array + { + // init + static $LANGUAGES = []; + + if ($LANGUAGES !== []) { + return $LANGUAGES; + } + + foreach ((new \ReflectionClass(__CLASS__))->getConstants() as $constant => $lang) { + if (\strpos($constant, 'EXTRA') !== false) { + $LANGUAGES[\strtolower($constant)] = $lang; + } else { + $LANGUAGES[\strtolower(\str_replace('_LANGUAGE_CODE', '', $constant))] = $lang; + } + } + + return $LANGUAGES; + } + + /** + * Returns an replacement array for ASCII methods. + * + * EXAMPLE: + * $array = ASCII::charsArray(); + * var_dump($array['ru']['б']); // 'b' + * + * + * @psalm-suppress InvalidNullableReturnType - we use the prepare* methods here, so we don't get NULL here + * + * @param bool $replace_extra_symbols [optional]

Add some more replacements e.g. "£" with " pound ".

+ * + * @psalm-pure + * + * @return array + * + * @phpstan-return array> + */ + public static function charsArray(bool $replace_extra_symbols = false): array + { + if ($replace_extra_symbols) { + self::prepareAsciiAndExtrasMaps(); + + return self::$ASCII_MAPS_AND_EXTRAS ?? []; + } + + self::prepareAsciiMaps(); + + return self::$ASCII_MAPS ?? []; + } + + /** + * Returns an replacement array for ASCII methods with a mix of multiple languages. + * + * EXAMPLE: + * $array = ASCII::charsArrayWithMultiLanguageValues(); + * var_dump($array['b']); // ['β', 'б', 'ဗ', 'ბ', 'ب'] + * + * + * @param bool $replace_extra_symbols [optional]

Add some more replacements e.g. "£" with " pound ".

+ * + * @psalm-pure + * + * @return array + *

An array of replacements.

+ * + * @phpstan-return array> + */ + public static function charsArrayWithMultiLanguageValues(bool $replace_extra_symbols = false): array + { + /** @var array>> */ + static $CHARS_ARRAY = []; + $cacheKey = '' . $replace_extra_symbols; + + if (isset($CHARS_ARRAY[$cacheKey])) { + return $CHARS_ARRAY[$cacheKey]; + } + + // init + $return = []; + $language_all_chars = self::charsArrayWithSingleLanguageValues( + $replace_extra_symbols, + false + ); + + /** @noinspection PhpSillyAssignmentInspection - hack for phpstan */ + /** @var array $language_all_chars */ + $language_all_chars = $language_all_chars; + + /** @noinspection AlterInForeachInspection */ + foreach ($language_all_chars as $key => &$value) { + $return[$value][] = $key; + } + + $CHARS_ARRAY[$cacheKey] = $return; + + /** @var array> $return - hack for phpstan */ + return $return; + } + + /** + * Returns an replacement array for ASCII methods with one language. + * + * For example, German will map 'ä' to 'ae', while other languages + * will simply return e.g. 'a'. + * + * EXAMPLE: + * $array = ASCII::charsArrayWithOneLanguage('ru'); + * $tmpKey = \array_search('yo', $array['replace']); + * echo $array['orig'][$tmpKey]; // 'ё' + * + * + * @psalm-suppress InvalidNullableReturnType - we use the prepare* methods here, so we don't get NULL here + * + * @param string $language [optional]

Language of the source string e.g.: en, de_at, or de-ch. + * (default is 'en') | ASCII::*_LANGUAGE_CODE

+ * @param bool $replace_extra_symbols [optional]

Add some more replacements e.g. "£" with " pound ".

+ * @param bool $asOrigReplaceArray [optional]

TRUE === return {orig: string[], replace: string[]} + * array

+ * + * @psalm-pure + * + * @return array + *

An array of replacements.

+ * + * @phpstan-param ASCII::*_LANGUAGE_CODE $language + * @phpstan-return array{orig: string[], replace: string[]}|array + */ + public static function charsArrayWithOneLanguage( + string $language = self::ENGLISH_LANGUAGE_CODE, + bool $replace_extra_symbols = false, + bool $asOrigReplaceArray = true + ): array { + $language = self::get_language($language); + + // init + /** @var array|array{orig: string[], replace: string[]}>> */ + static $CHARS_ARRAY = []; + $cacheKey = '' . $replace_extra_symbols . '-' . $asOrigReplaceArray; + + // check static cache + if (isset($CHARS_ARRAY[$cacheKey][$language])) { + return $CHARS_ARRAY[$cacheKey][$language]; + } + + if ($replace_extra_symbols) { + self::prepareAsciiAndExtrasMaps(); + + /** @noinspection DuplicatedCode */ + if (isset(self::$ASCII_MAPS_AND_EXTRAS[$language])) { + $tmpArray = self::$ASCII_MAPS_AND_EXTRAS[$language]; + + if ($asOrigReplaceArray) { + $CHARS_ARRAY[$cacheKey][$language] = [ + 'orig' => \array_keys($tmpArray), + 'replace' => \array_values($tmpArray), + ]; + } else { + $CHARS_ARRAY[$cacheKey][$language] = $tmpArray; + } + } else { + /** @noinspection NestedPositiveIfStatementsInspection */ + if ($asOrigReplaceArray) { + $CHARS_ARRAY[$cacheKey][$language] = [ + 'orig' => [], + 'replace' => [], + ]; + } else { + $CHARS_ARRAY[$cacheKey][$language] = []; + } + } + } else { + self::prepareAsciiMaps(); + + /** @noinspection DuplicatedCode */ + if (isset(self::$ASCII_MAPS[$language])) { + $tmpArray = self::$ASCII_MAPS[$language]; + + if ($asOrigReplaceArray) { + $CHARS_ARRAY[$cacheKey][$language] = [ + 'orig' => \array_keys($tmpArray), + 'replace' => \array_values($tmpArray), + ]; + } else { + $CHARS_ARRAY[$cacheKey][$language] = $tmpArray; + } + } else { + /** @noinspection NestedPositiveIfStatementsInspection */ + if ($asOrigReplaceArray) { + $CHARS_ARRAY[$cacheKey][$language] = [ + 'orig' => [], + 'replace' => [], + ]; + } else { + $CHARS_ARRAY[$cacheKey][$language] = []; + } + } + } + + return $CHARS_ARRAY[$cacheKey][$language] ?? ['orig' => [], 'replace' => []]; + } + + /** + * Returns an replacement array for ASCII methods with multiple languages. + * + * EXAMPLE: + * $array = ASCII::charsArrayWithSingleLanguageValues(); + * $tmpKey = \array_search('hnaik', $array['replace']); + * echo $array['orig'][$tmpKey]; // '၌' + * + * + * @param bool $replace_extra_symbols [optional]

Add some more replacements e.g. "£" with " pound ".

+ * @param bool $asOrigReplaceArray [optional]

TRUE === return {orig: string[], replace: string[]} + * array

+ * + * @psalm-pure + * + * @return array + *

An array of replacements.

+ * + * @phpstan-return array{orig: string[], replace: string[]}|array + */ + public static function charsArrayWithSingleLanguageValues( + bool $replace_extra_symbols = false, + bool $asOrigReplaceArray = true + ): array { + // init + /** @var array|array{orig: string[], replace: string[]}> */ + static $CHARS_ARRAY = []; + $cacheKey = '' . $replace_extra_symbols . '-' . $asOrigReplaceArray; + + if (isset($CHARS_ARRAY[$cacheKey])) { + return $CHARS_ARRAY[$cacheKey]; + } + + if ($replace_extra_symbols) { + self::prepareAsciiAndExtrasMaps(); + + /** @noinspection AlterInForeachInspection */ + /** @psalm-suppress PossiblyNullIterator - we use the prepare* methods here, so we don't get NULL here */ + foreach (self::$ASCII_MAPS_AND_EXTRAS ?? [] as &$map) { + $CHARS_ARRAY[$cacheKey][] = $map; + } + } else { + self::prepareAsciiMaps(); + + /** @noinspection AlterInForeachInspection */ + /** @psalm-suppress PossiblyNullIterator - we use the prepare* methods here, so we don't get NULL here */ + foreach (self::$ASCII_MAPS ?? [] as &$map) { + $CHARS_ARRAY[$cacheKey][] = $map; + } + } + + /** @phpstan-ignore-next-line - ... error? */ + $CHARS_ARRAY[$cacheKey] = \array_merge([], ...$CHARS_ARRAY[$cacheKey]); + + if ($asOrigReplaceArray) { + $CHARS_ARRAY[$cacheKey] = [ + 'orig' => \array_keys($CHARS_ARRAY[$cacheKey]), + 'replace' => \array_values($CHARS_ARRAY[$cacheKey]), + ]; + } + + return $CHARS_ARRAY[$cacheKey]; + } + + /** + * Accepts a string and removes all non-UTF-8 characters from it + extras if needed. + * + * @param string $str

The string to be sanitized.

+ * @param bool $normalize_whitespace [optional]

Set to true, if you need to normalize the + * whitespace.

+ * @param bool $normalize_msword [optional]

Set to true, if you need to normalize MS Word chars + * e.g.: "…" + * => "..."

+ * @param bool $keep_non_breaking_space [optional]

Set to true, to keep non-breaking-spaces, in + * combination with + * $normalize_whitespace

+ * @param bool $remove_invisible_characters [optional]

Set to false, if you not want to remove invisible + * characters e.g.: "\0"

+ * + * @psalm-pure + * + * @return string + *

A clean UTF-8 string.

+ */ + public static function clean( + string $str, + bool $normalize_whitespace = true, + bool $keep_non_breaking_space = false, + bool $normalize_msword = true, + bool $remove_invisible_characters = true + ): string { + // http://stackoverflow.com/questions/1401317/remove-non-utf8-characters-from-string + // caused connection reset problem on larger strings + + $regex = '/ + ( + (?: [\x00-\x7F] # single-byte sequences 0xxxxxxx + | [\xC0-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx + | [\xE0-\xEF][\x80-\xBF]{2} # triple-byte sequences 1110xxxx 10xxxxxx * 2 + | [\xF0-\xF7][\x80-\xBF]{3} # quadruple-byte sequence 11110xxx 10xxxxxx * 3 + ){1,100} # ...one or more times + ) + | ( [\x80-\xBF] ) # invalid byte in range 10000000 - 10111111 + | ( [\xC0-\xFF] ) # invalid byte in range 11000000 - 11111111 + /x'; + $str = (string) \preg_replace($regex, '$1', $str); + + if ($normalize_whitespace) { + $str = self::normalize_whitespace($str, $keep_non_breaking_space); + } + + if ($normalize_msword) { + $str = self::normalize_msword($str); + } + + if ($remove_invisible_characters) { + $str = self::remove_invisible_characters($str); + } + + return $str; + } + + /** + * Checks if a string is 7 bit ASCII. + * + * EXAMPLE: + * ASCII::is_ascii('白'); // false + * + * + * @param string $str

The string to check.

+ * + * @psalm-pure + * + * @return bool + *

+ * true if it is ASCII
+ * false otherwise + *

+ */ + public static function is_ascii(string $str): bool + { + if ($str === '') { + return true; + } + + return !\preg_match('/' . self::$REGEX_ASCII . '/', $str); + } + + /** + * Returns a string with smart quotes, ellipsis characters, and dashes from + * Windows-1252 (commonly used in Word documents) replaced by their ASCII + * equivalents. + * + * EXAMPLE: + * ASCII::normalize_msword('„Abcdef…”'); // '"Abcdef..."' + * + * + * @param string $str

The string to be normalized.

+ * + * @psalm-pure + * + * @return string + *

A string with normalized characters for commonly used chars in Word documents.

+ */ + public static function normalize_msword(string $str): string + { + if ($str === '') { + return ''; + } + + /** @var array{orig: string[], replace: string[]} */ + static $MSWORD_CACHE = ['orig' => [], 'replace' => []]; + + if (empty($MSWORD_CACHE['orig'])) { + self::prepareAsciiMaps(); + + /** @var array */ + $map = self::$ASCII_MAPS[self::EXTRA_MSWORD_CHARS_LANGUAGE_CODE] ?? []; + + $MSWORD_CACHE = [ + 'orig' => \array_keys($map), + 'replace' => \array_values($map), + ]; + } + + return \str_replace($MSWORD_CACHE['orig'], $MSWORD_CACHE['replace'], $str); + } + + /** + * Normalize the whitespace. + * + * EXAMPLE: + * ASCII::normalize_whitespace("abc-\xc2\xa0-öäü-\xe2\x80\xaf-\xE2\x80\xAC", true); // "abc-\xc2\xa0-öäü- -" + * + * + * @param string $str

The string to be normalized.

+ * @param bool $keepNonBreakingSpace [optional]

Set to true, to keep non-breaking-spaces.

+ * @param bool $keepBidiUnicodeControls [optional]

Set to true, to keep non-printable (for the web) + * bidirectional text chars.

+ * @param bool $normalize_control_characters [optional]

Set to true, to convert e.g. LINE-, PARAGRAPH-SEPARATOR with "\n" and LINE TABULATION with "\t".

+ * + * @psalm-pure + * + * @return string + *

A string with normalized whitespace.

+ */ + public static function normalize_whitespace( + string $str, + bool $keepNonBreakingSpace = false, + bool $keepBidiUnicodeControls = false, + bool $normalize_control_characters = false + ): string { + if ($str === '') { + return ''; + } + + /** @var array> */ + static $WHITESPACE_CACHE = []; + $cacheKey = (int) $keepNonBreakingSpace; + + if ($normalize_control_characters) { + $str = \str_replace( + [ + "\x0d\x0c", // 'END OF LINE' + "\xe2\x80\xa8", // 'LINE SEPARATOR' + "\xe2\x80\xa9", // 'PARAGRAPH SEPARATOR' + "\x0c", // 'FORM FEED' // "\f" + "\x0b", // 'VERTICAL TAB' // "\v" + ], + [ + "\n", + "\n", + "\n", + "\n", + "\t", + ], + $str + ); + } + + if (!isset($WHITESPACE_CACHE[$cacheKey])) { + self::prepareAsciiMaps(); + + $WHITESPACE_CACHE[$cacheKey] = self::$ASCII_MAPS[self::EXTRA_WHITESPACE_CHARS_LANGUAGE_CODE] ?? []; + + if ($keepNonBreakingSpace) { + unset($WHITESPACE_CACHE[$cacheKey]["\xc2\xa0"]); + } + + $WHITESPACE_CACHE[$cacheKey] = array_keys($WHITESPACE_CACHE[$cacheKey]); + } + + if (!$keepBidiUnicodeControls) { + /** @var array|null */ + static $BIDI_UNICODE_CONTROLS_CACHE = null; + + if ($BIDI_UNICODE_CONTROLS_CACHE === null) { + $BIDI_UNICODE_CONTROLS_CACHE = self::$BIDI_UNI_CODE_CONTROLS_TABLE; + } + + $str = \str_replace($BIDI_UNICODE_CONTROLS_CACHE, '', $str); + } + + return \str_replace($WHITESPACE_CACHE[$cacheKey], ' ', $str); + } + + /** + * Remove invisible characters from a string. + * + * e.g.: This prevents sandwiching null characters between ascii characters, like Java\0script. + * + * copy&past from https://github.com/bcit-ci/CodeIgniter/blob/develop/system/core/Common.php + * + * @param string $str + * @param bool $url_encoded + * @param string $replacement + * @param bool $keep_basic_control_characters + * + * @psalm-pure + * + * @return string + */ + public static function remove_invisible_characters( + string $str, + bool $url_encoded = false, + string $replacement = '', + bool $keep_basic_control_characters = true + ): string { + // init + $non_displayables = []; + + // every control character except: + // - newline (dec 10), + // - carriage return (dec 13), + // - horizontal tab (dec 09) + if ($url_encoded) { + $non_displayables[] = '/%0[0-8bcefBCEF]/'; // url encoded 00-08, 11, 12, 14, 15 + $non_displayables[] = '/%1[0-9a-fA-F]/'; // url encoded 16-31 + } + + if ($keep_basic_control_characters) { + $non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S'; // 00-08, 11, 12, 14-31, 127 + } else { + $str = self::normalize_whitespace($str, false, false, true); + $non_displayables[] = '/[^\P{C}\s]/u'; + } + + do { + $str = (string) \preg_replace($non_displayables, $replacement, $str, -1, $count); + } while ($count !== 0); + + return $str; + } + + /** + * WARNING: This method will return broken characters and is only for special cases. + * + * Convert two UTF-8 encoded string to a single-byte strings suitable for + * functions that need the same string length after the conversion. + * + * The function simply uses (and updates) a tailored dynamic encoding + * (in/out map parameter) where non-ascii characters are remapped to + * the range [128-255] in order of appearance. + * + * @param string $str1 + * @param string $str2 + * + * @return string[] + * + * @phpstan-return array{0: string, 1: string} + */ + public static function to_ascii_remap(string $str1, string $str2): array + { + $charMap = []; + $str1 = self::to_ascii_remap_intern($str1, $charMap); + $str2 = self::to_ascii_remap_intern($str2, $charMap); + + return [$str1, $str2]; + } + + /** + * Returns an ASCII version of the string. A set of non-ASCII characters are + * replaced with their closest ASCII counterparts, and the rest are removed + * by default. The language or locale of the source string can be supplied + * for language-specific transliteration in any of the following formats: + * en, en_GB, or en-GB. For example, passing "de" results in "äöü" mapping + * to "aeoeue" rather than "aou" as in other languages. + * + * EXAMPLE: + * ASCII::to_ascii('�Düsseldorf�', 'en'); // Dusseldorf + * + * + * @param string $str

The input string.

+ * @param string $language [optional]

Language of the source string. + * (default is 'en') | ASCII::*_LANGUAGE_CODE

+ * @param bool $remove_unsupported_chars [optional]

Whether or not to remove the + * unsupported characters.

+ * @param bool $replace_extra_symbols [optional]

Add some more replacements e.g. "£" with " pound + * ".

+ * @param bool $use_transliterate [optional]

Use ASCII::to_transliterate() for unknown chars.

+ * @param bool|null $replace_single_chars_only [optional]

Single char replacement is better for the + * performance, but some languages need to replace more then one char + * at the same time. | NULL === auto-setting, depended on the + * language

+ * + * @psalm-pure + * + * @return string + *

A string that contains only ASCII characters.

+ * + * @phpstan-param ASCII::*_LANGUAGE_CODE $language + */ + public static function to_ascii( + string $str, + string $language = self::ENGLISH_LANGUAGE_CODE, + bool $remove_unsupported_chars = true, + bool $replace_extra_symbols = false, + bool $use_transliterate = false, + bool $replace_single_chars_only = null + ): string { + if ($str === '') { + return ''; + } + + /** @phpstan-var ASCII::*_LANGUAGE_CODE - hack for phpstan */ + $language = self::get_language($language); + + static $EXTRA_SYMBOLS_CACHE = null; + + /** @var array> */ + static $REPLACE_HELPER_CACHE = []; + $cacheKey = $language . '-' . $replace_extra_symbols; + + if (!isset($REPLACE_HELPER_CACHE[$cacheKey])) { + $langAll = self::charsArrayWithSingleLanguageValues($replace_extra_symbols, false); + + $langSpecific = self::charsArrayWithOneLanguage($language, $replace_extra_symbols, false); + + if ($langSpecific === []) { + $REPLACE_HELPER_CACHE[$cacheKey] = $langAll; + } else { + $REPLACE_HELPER_CACHE[$cacheKey] = \array_merge([], $langAll, $langSpecific); + } + } + + if ( + $replace_extra_symbols + && + $EXTRA_SYMBOLS_CACHE === null + ) { + $EXTRA_SYMBOLS_CACHE = []; + foreach (self::$ASCII_EXTRAS ?? [] as $extrasDataTmp) { + foreach ($extrasDataTmp as $extrasDataKeyTmp => $extrasDataValueTmp) { + $EXTRA_SYMBOLS_CACHE[$extrasDataKeyTmp] = $extrasDataKeyTmp; + } + } + $EXTRA_SYMBOLS_CACHE = \implode('', $EXTRA_SYMBOLS_CACHE); + } + + $charDone = []; + if (\preg_match_all('/' . self::$REGEX_ASCII . ($replace_extra_symbols ? '|[' . $EXTRA_SYMBOLS_CACHE . ']' : '') . '/u', $str, $matches)) { + if (!$replace_single_chars_only) { + if (self::$LANGUAGE_MAX_KEY === null) { + self::$LANGUAGE_MAX_KEY = self::getData('ascii_language_max_key'); + } + + $maxKeyLength = self::$LANGUAGE_MAX_KEY[$language] ?? 0; + + if ($maxKeyLength >= 5) { + foreach ($matches[0] as $keyTmp => $char) { + if (isset($matches[0][$keyTmp + 4])) { + $fiveChars = $matches[0][$keyTmp + 0] . $matches[0][$keyTmp + 1] . $matches[0][$keyTmp + 2] . $matches[0][$keyTmp + 3] . $matches[0][$keyTmp + 4]; + } else { + $fiveChars = null; + } + if ( + $fiveChars + && + !isset($charDone[$fiveChars]) + && + isset($REPLACE_HELPER_CACHE[$cacheKey][$fiveChars]) + && + \strpos($str, $fiveChars) !== false + ) { + // DEBUG + //\var_dump($str, $fiveChars, $REPLACE_HELPER_CACHE[$cacheKey][$fiveChars]); + + $charDone[$fiveChars] = true; + $str = \str_replace($fiveChars, $REPLACE_HELPER_CACHE[$cacheKey][$fiveChars], $str); + + // DEBUG + //\var_dump($str, "\n"); + } + } + } + + if ($maxKeyLength >= 4) { + foreach ($matches[0] as $keyTmp => $char) { + if (isset($matches[0][$keyTmp + 3])) { + $fourChars = $matches[0][$keyTmp + 0] . $matches[0][$keyTmp + 1] . $matches[0][$keyTmp + 2] . $matches[0][$keyTmp + 3]; + } else { + $fourChars = null; + } + if ( + $fourChars + && + !isset($charDone[$fourChars]) + && + isset($REPLACE_HELPER_CACHE[$cacheKey][$fourChars]) + && + \strpos($str, $fourChars) !== false + ) { + // DEBUG + //\var_dump($str, $fourChars, $REPLACE_HELPER_CACHE[$cacheKey][$fourChars]); + + $charDone[$fourChars] = true; + $str = \str_replace($fourChars, $REPLACE_HELPER_CACHE[$cacheKey][$fourChars], $str); + + // DEBUG + //\var_dump($str, "\n"); + } + } + } + + foreach ($matches[0] as $keyTmp => $char) { + if (isset($matches[0][$keyTmp + 2])) { + $threeChars = $matches[0][$keyTmp + 0] . $matches[0][$keyTmp + 1] . $matches[0][$keyTmp + 2]; + } else { + $threeChars = null; + } + if ( + $threeChars + && + !isset($charDone[$threeChars]) + && + isset($REPLACE_HELPER_CACHE[$cacheKey][$threeChars]) + && + \strpos($str, $threeChars) !== false + ) { + // DEBUG + //\var_dump($str, $threeChars, $REPLACE_HELPER_CACHE[$cacheKey][$threeChars]); + + $charDone[$threeChars] = true; + $str = \str_replace($threeChars, $REPLACE_HELPER_CACHE[$cacheKey][$threeChars], $str); + + // DEBUG + //\var_dump($str, "\n"); + } + } + + foreach ($matches[0] as $keyTmp => $char) { + if (isset($matches[0][$keyTmp + 1])) { + $twoChars = $matches[0][$keyTmp + 0] . $matches[0][$keyTmp + 1]; + } else { + $twoChars = null; + } + if ( + $twoChars + && + !isset($charDone[$twoChars]) + && + isset($REPLACE_HELPER_CACHE[$cacheKey][$twoChars]) + && + \strpos($str, $twoChars) !== false + ) { + // DEBUG + //\var_dump($str, $twoChars, $REPLACE_HELPER_CACHE[$cacheKey][$twoChars]); + + $charDone[$twoChars] = true; + $str = \str_replace($twoChars, $REPLACE_HELPER_CACHE[$cacheKey][$twoChars], $str); + + // DEBUG + //\var_dump($str, "\n"); + } + } + } + + foreach ($matches[0] as $char) { + if ( + !isset($charDone[$char]) + && + isset($REPLACE_HELPER_CACHE[$cacheKey][$char]) + && + \strpos($str, $char) !== false + ) { + // DEBUG + //\var_dump($str, $char, $REPLACE_HELPER_CACHE[$cacheKey][$char]); + + $charDone[$char] = true; + $str = \str_replace($char, $REPLACE_HELPER_CACHE[$cacheKey][$char], $str); + + // DEBUG + //\var_dump($str, "\n"); + } + } + } + + /** @psalm-suppress PossiblyNullOperand - we use the prepare* methods here, so we don't get NULL here */ + if (!isset(self::$ASCII_MAPS[$language])) { + $use_transliterate = true; + } + + if ($use_transliterate) { + $str = self::to_transliterate($str, null, false); + } + + if ($remove_unsupported_chars) { + $str = (string) \str_replace(["\n\r", "\n", "\r", "\t"], ' ', $str); + $str = (string) \preg_replace('/' . self::$REGEX_ASCII . '/', '', $str); + } + + return $str; + } + + /** + * Convert given string to safe filename (and keep string case). + * + * EXAMPLE: + * ASCII::to_filename('שדגשדג.png', true)); // 'shdgshdg.png' + * + * + * @param string $str + * @param bool $use_transliterate

ASCII::to_transliterate() is used by default - unsafe characters are + * simply replaced with hyphen otherwise.

+ * @param string $fallback_char + * + * @psalm-pure + * + * @return string + *

A string that contains only safe characters for a filename.

+ */ + public static function to_filename( + string $str, + bool $use_transliterate = true, + string $fallback_char = '-' + ): string { + if ($use_transliterate) { + $str = self::to_transliterate($str, $fallback_char); + } + + $fallback_char_escaped = \preg_quote($fallback_char, '/'); + + $str = (string) \preg_replace( + [ + '/[^' . $fallback_char_escaped . '.\\-a-zA-Z\d\\s]/', // 1) remove un-needed chars + '/\s+/u', // 2) convert spaces to $fallback_char + '/[' . $fallback_char_escaped . ']+/u', // 3) remove double $fallback_char's + ], + [ + '', + $fallback_char, + $fallback_char, + ], + $str + ); + + return \trim($str, $fallback_char); + } + + /** + * Converts the string into an URL slug. This includes replacing non-ASCII + * characters with their closest ASCII equivalents, removing remaining + * non-ASCII and non-alphanumeric characters, and replacing whitespace with + * $separator. The separator defaults to a single dash, and the string + * is also converted to lowercase. The language of the source string can + * also be supplied for language-specific transliteration. + * + * @param string $str + * @param string $separator [optional]

The string used to replace whitespace.

+ * @param string $language [optional]

Language of the source string. + * (default is 'en') | ASCII::*_LANGUAGE_CODE

+ * @param array $replacements [optional]

A map of replaceable strings.

+ * @param bool $replace_extra_symbols [optional]

Add some more replacements e.g. "£" with " + * pound ".

+ * @param bool $use_str_to_lower [optional]

Use "string to lower" for the input.

+ * @param bool $use_transliterate [optional]

Use ASCII::to_transliterate() for unknown + * chars.

+ * @psalm-pure + * + * @return string + *

A string that has been converted to an URL slug.

+ * + * @phpstan-param ASCII::*_LANGUAGE_CODE $language + */ + public static function to_slugify( + string $str, + string $separator = '-', + string $language = self::ENGLISH_LANGUAGE_CODE, + array $replacements = [], + bool $replace_extra_symbols = false, + bool $use_str_to_lower = true, + bool $use_transliterate = false + ): string { + if ($str === '') { + return ''; + } + + foreach ($replacements as $from => $to) { + $str = \str_replace($from, $to, $str); + } + + $str = self::to_ascii( + $str, + $language, + false, + $replace_extra_symbols, + $use_transliterate + ); + + $str = \str_replace('@', $separator, $str); + + $str = (string) \preg_replace( + '/[^a-zA-Z\\d\\s\\-_' . \preg_quote($separator, '/') . ']/', + '', + $str + ); + + if ($use_str_to_lower) { + $str = \strtolower($str); + } + + $str = (string) \preg_replace('/^[\'\\s]+|[\'\\s]+$/', '', $str); + $str = (string) \preg_replace('/\\B([A-Z])/', '-\1', $str); + $str = (string) \preg_replace('/[\\-_\\s]+/', $separator, $str); + + $l = \strlen($separator); + if ($l && \strpos($str, $separator) === 0) { + $str = (string) \substr($str, $l); + } + + if (\substr($str, -$l) === $separator) { + $str = (string) \substr($str, 0, \strlen($str) - $l); + } + + return $str; + } + + /** + * Returns an ASCII version of the string. A set of non-ASCII characters are + * replaced with their closest ASCII counterparts, and the rest are removed + * unless instructed otherwise. + * + * EXAMPLE: + * ASCII::to_transliterate('déjà σσς iıii'); // 'deja sss iiii' + * + * + * @param string $str

The input string.

+ * @param string|null $unknown [optional]

Character use if character unknown. (default is '?') + * But you can also use NULL to keep the unknown chars.

+ * @param bool $strict [optional]

Use "transliterator_transliterate()" from PHP-Intl + * + * @psalm-pure + * + * @return string + *

A String that contains only ASCII characters.

+ * + * @noinspection ParameterDefaultValueIsNotNullInspection + */ + public static function to_transliterate( + string $str, + $unknown = '?', + bool $strict = false + ): string { + /** @var array|null */ + static $UTF8_TO_TRANSLIT = null; + + /** null|\Transliterator */ + static $TRANSLITERATOR = null; + + /** @var bool|null */ + static $SUPPORT_INTL = null; + + if ($str === '') { + return ''; + } + + if ($SUPPORT_INTL === null) { + $SUPPORT_INTL = \extension_loaded('intl'); + } + + // check if we only have ASCII, first (better performance) + $str_tmp = $str; + if (self::is_ascii($str)) { + return $str; + } + + $str = self::clean($str); + + // check again, if we only have ASCII, now ... + if ( + $str_tmp !== $str + && + self::is_ascii($str) + ) { + return $str; + } + + if ( + $strict + && + $SUPPORT_INTL === true + ) { + if (!isset($TRANSLITERATOR)) { + // INFO: see "*-Latin" rules via "transliterator_list_ids()" + /** @var \Transliterator */ + $TRANSLITERATOR = \transliterator_create('NFKC; [:Nonspacing Mark:] Remove; NFKC; Any-Latin; Latin-ASCII;'); + } + + // INFO: https://unicode.org/cldr/utility/character.jsp + $str_tmp = \transliterator_transliterate($TRANSLITERATOR, $str); + + if ($str_tmp !== false) { + + // check again, if we only have ASCII, now ... + if ( + $str_tmp !== $str + && + self::is_ascii($str_tmp) + ) { + return $str_tmp; + } + + $str = $str_tmp; + } + } + + if (self::$ORD === null) { + self::$ORD = self::getData('ascii_ord'); + } + + \preg_match_all('/.|[^\x00]$/us', $str, $array_tmp); + $chars = $array_tmp[0]; + $ord = null; + $str_tmp = ''; + foreach ($chars as &$c) { + $ordC0 = self::$ORD[$c[0]]; + + if ($ordC0 >= 0 && $ordC0 <= 127) { + $str_tmp .= $c; + + continue; + } + + $ordC1 = self::$ORD[$c[1]]; + + // ASCII - next please + if ($ordC0 >= 192 && $ordC0 <= 223) { + $ord = ($ordC0 - 192) * 64 + ($ordC1 - 128); + } + + if ($ordC0 >= 224) { + $ordC2 = self::$ORD[$c[2]]; + + if ($ordC0 <= 239) { + $ord = ($ordC0 - 224) * 4096 + ($ordC1 - 128) * 64 + ($ordC2 - 128); + } + + if ($ordC0 >= 240) { + $ordC3 = self::$ORD[$c[3]]; + + if ($ordC0 <= 247) { + $ord = ($ordC0 - 240) * 262144 + ($ordC1 - 128) * 4096 + ($ordC2 - 128) * 64 + ($ordC3 - 128); + } + + // We only process valid UTF-8 chars (<= 4 byte), so we don't need this code here ... + /* + if ($ordC0 >= 248) { + $ordC4 = self::$ORD[$c[4]]; + + if ($ordC0 <= 251) { + $ord = ($ordC0 - 248) * 16777216 + ($ordC1 - 128) * 262144 + ($ordC2 - 128) * 4096 + ($ordC3 - 128) * 64 + ($ordC4 - 128); + } + + if ($ordC0 >= 252) { + $ordC5 = self::$ORD[$c[5]]; + + if ($ordC0 <= 253) { + $ord = ($ordC0 - 252) * 1073741824 + ($ordC1 - 128) * 16777216 + ($ordC2 - 128) * 262144 + ($ordC3 - 128) * 4096 + ($ordC4 - 128) * 64 + ($ordC5 - 128); + } + } + } + */ + } + } + + if ( + $ordC0 === 254 + || + $ordC0 === 255 + || + $ord === null + ) { + $str_tmp .= $unknown ?? $c; + + continue; + } + + $bank = $ord >> 8; + if (!isset($UTF8_TO_TRANSLIT[$bank])) { + $UTF8_TO_TRANSLIT[$bank] = self::getDataIfExists(\sprintf('x%03x', $bank)); + } + + $new_char = $ord & 255; + + if (isset($UTF8_TO_TRANSLIT[$bank][$new_char])) { + + // keep for debugging + /* + echo "file: " . sprintf('x%02x', $bank) . "\n"; + echo "char: " . $c . "\n"; + echo "ord: " . $ord . "\n"; + echo "new_char: " . $new_char . "\n"; + echo "new_char: " . mb_chr($new_char) . "\n"; + echo "ascii: " . $UTF8_TO_TRANSLIT[$bank][$new_char] . "\n"; + echo "bank:" . $bank . "\n\n"; + */ + + $new_char = $UTF8_TO_TRANSLIT[$bank][$new_char]; + + /** @noinspection MissingOrEmptyGroupStatementInspection */ + /** @noinspection PhpStatementHasEmptyBodyInspection */ + if ($unknown === null && $new_char === '') { + // nothing + } elseif ( + $new_char === '[?]' + || + $new_char === '[?] ' + ) { + $c = $unknown ?? $c; + } else { + $c = $new_char; + } + } else { + + // keep for debugging missing chars + /* + echo "file: " . sprintf('x%02x', $bank) . "\n"; + echo "char: " . $c . "\n"; + echo "ord: " . $ord . "\n"; + echo "new_char: " . $new_char . "\n"; + echo "new_char: " . mb_chr($new_char) . "\n"; + echo "bank:" . $bank . "\n\n"; + */ + + $c = $unknown ?? $c; + } + + $str_tmp .= $c; + } + + return $str_tmp; + } + + /** + * WARNING: This method will return broken characters and is only for special cases. + * + * Convert a UTF-8 encoded string to a single-byte string suitable for + * functions that need the same string length after the conversion. + * + * The function simply uses (and updates) a tailored dynamic encoding + * (in/out map parameter) where non-ascii characters are remapped to + * the range [128-255] in order of appearance. + * + * Thus, it supports up to 128 different multibyte code points max over + * the whole set of strings sharing this encoding. + * + * Source: https://github.com/KEINOS/mb_levenshtein + * + * @param string $str

UTF-8 string to be converted to extended ASCII.

+ * @param array $map

Internal-Map of code points to ASCII characters.

+ * + * @return string + *

Mapped borken string.

+ * + * @phpstan-param array $map + */ + private static function to_ascii_remap_intern(string $str, array &$map): string + { + // find all utf-8 characters + $matches = []; + if (!\preg_match_all('/[\xC0-\xF7][\x80-\xBF]+/', $str, $matches)) { + return $str; // plain ascii string + } + + // update the encoding map with the characters not already met + $mapCount = \count($map); + foreach ($matches[0] as $mbc) { + if (!isset($map[$mbc])) { + $map[$mbc] = \chr(128 + $mapCount); + ++$mapCount; + } + } + + // finally, remap non-ascii characters + return \strtr($str, $map); + } + + /** + * Get the language from a string. + * + * e.g.: de_at -> de_at + * de_DE -> de + * DE_DE -> de + * de-de -> de + * + * @noinspection ReturnTypeCanBeDeclaredInspection + * + * @param string $language + * + * @psalm-pure + * + * @return string + */ + private static function get_language(string $language) + { + if ($language === '') { + return ''; + } + + if ( + \strpos($language, '_') === false + && + \strpos($language, '-') === false + ) { + return \strtolower($language); + } + + $language = \str_replace('-', '_', \strtolower($language)); + + $regex = '/(?[a-z]+)_\g{first}/'; + + return (string) \preg_replace($regex, '$1', $language); + } + + /** + * Get data from "/data/*.php". + * + * @noinspection ReturnTypeCanBeDeclaredInspection + * + * @param string $file + * + * @psalm-pure + * + * @return array + */ + private static function getData(string $file) + { + /** @noinspection PhpIncludeInspection */ + /** @noinspection UsingInclusionReturnValueInspection */ + /** @psalm-suppress UnresolvableInclude */ + return include __DIR__ . '/data/' . $file . '.php'; + } + + /** + * Get data from "/data/*.php". + * + * @param string $file + * + * @psalm-pure + * + * @return array + */ + private static function getDataIfExists(string $file): array + { + $file = __DIR__ . '/data/' . $file . '.php'; + /** @psalm-suppress ImpureFunctionCall */ + if (\is_file($file)) { + /** @noinspection PhpIncludeInspection */ + /** @noinspection UsingInclusionReturnValueInspection */ + /** @psalm-suppress UnresolvableInclude */ + return include $file; + } + + return []; + } + + /** + * @psalm-pure + * + * @return void + */ + private static function prepareAsciiAndExtrasMaps() + { + if (self::$ASCII_MAPS_AND_EXTRAS === null) { + self::prepareAsciiMaps(); + self::prepareAsciiExtras(); + + /** @psalm-suppress PossiblyNullArgument - we use the prepare* methods here, so we don't get NULL here */ + self::$ASCII_MAPS_AND_EXTRAS = \array_merge_recursive( + self::$ASCII_MAPS ?? [], + self::$ASCII_EXTRAS ?? [] + ); + } + } + + /** + * @psalm-pure + * + * @return void + */ + private static function prepareAsciiMaps() + { + if (self::$ASCII_MAPS === null) { + self::$ASCII_MAPS = self::getData('ascii_by_languages'); + } + } + + /** + * @psalm-pure + * + * @return void + */ + private static function prepareAsciiExtras() + { + if (self::$ASCII_EXTRAS === null) { + self::$ASCII_EXTRAS = self::getData('ascii_extras_by_languages'); + } + } +} \ No newline at end of file diff --git a/includes/libraries/anti-xss-master/src/voku/helper/UTF8.php b/includes/libraries/anti-xss-master/src/voku/helper/UTF8.php new file mode 100644 index 000000000..658b11ab6 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/UTF8.php @@ -0,0 +1,14100 @@ + Byte-Length + * + * INFO: https://en.wikipedia.org/wiki/Byte_order_mark + * + * @var array + */ + private static $BOM = [ + "\xef\xbb\xbf" => 3, // UTF-8 BOM + '' => 6, // UTF-8 BOM as "WINDOWS-1252" (one char has [maybe] more then one byte ...) + "\x00\x00\xfe\xff" => 4, // UTF-32 (BE) BOM + ' þÿ' => 6, // UTF-32 (BE) BOM as "WINDOWS-1252" + "\xff\xfe\x00\x00" => 4, // UTF-32 (LE) BOM + 'ÿþ ' => 6, // UTF-32 (LE) BOM as "WINDOWS-1252" + "\xfe\xff" => 2, // UTF-16 (BE) BOM + 'þÿ' => 4, // UTF-16 (BE) BOM as "WINDOWS-1252" + "\xff\xfe" => 2, // UTF-16 (LE) BOM + 'ÿþ' => 4, // UTF-16 (LE) BOM as "WINDOWS-1252" + ]; + + /** + * Numeric code point => UTF-8 Character + * + * url: http://www.w3schools.com/charsets/ref_utf_punctuation.asp + * + * @var array + */ + private static $WHITESPACE = [ + // NULL Byte + 0 => "\x0", + // Tab + 9 => "\x9", + // New Line + 10 => "\xa", + // Vertical Tab + 11 => "\xb", + // Carriage Return + 13 => "\xd", + // Ordinary Space + 32 => "\x20", + // NO-BREAK SPACE + 160 => "\xc2\xa0", + // OGHAM SPACE MARK + 5760 => "\xe1\x9a\x80", + // MONGOLIAN VOWEL SEPARATOR + 6158 => "\xe1\xa0\x8e", + // EN QUAD + 8192 => "\xe2\x80\x80", + // EM QUAD + 8193 => "\xe2\x80\x81", + // EN SPACE + 8194 => "\xe2\x80\x82", + // EM SPACE + 8195 => "\xe2\x80\x83", + // THREE-PER-EM SPACE + 8196 => "\xe2\x80\x84", + // FOUR-PER-EM SPACE + 8197 => "\xe2\x80\x85", + // SIX-PER-EM SPACE + 8198 => "\xe2\x80\x86", + // FIGURE SPACE + 8199 => "\xe2\x80\x87", + // PUNCTUATION SPACE + 8200 => "\xe2\x80\x88", + // THIN SPACE + 8201 => "\xe2\x80\x89", + // HAIR SPACE + 8202 => "\xe2\x80\x8a", + // LINE SEPARATOR + 8232 => "\xe2\x80\xa8", + // PARAGRAPH SEPARATOR + 8233 => "\xe2\x80\xa9", + // NARROW NO-BREAK SPACE + 8239 => "\xe2\x80\xaf", + // MEDIUM MATHEMATICAL SPACE + 8287 => "\xe2\x81\x9f", + // HALFWIDTH HANGUL FILLER + 65440 => "\xef\xbe\xa0", + // IDEOGRAPHIC SPACE + 12288 => "\xe3\x80\x80", + ]; + + /** + * @var array + */ + private static $WHITESPACE_TABLE = [ + 'SPACE' => "\x20", + 'NO-BREAK SPACE' => "\xc2\xa0", + 'OGHAM SPACE MARK' => "\xe1\x9a\x80", + 'EN QUAD' => "\xe2\x80\x80", + 'EM QUAD' => "\xe2\x80\x81", + 'EN SPACE' => "\xe2\x80\x82", + 'EM SPACE' => "\xe2\x80\x83", + 'THREE-PER-EM SPACE' => "\xe2\x80\x84", + 'FOUR-PER-EM SPACE' => "\xe2\x80\x85", + 'SIX-PER-EM SPACE' => "\xe2\x80\x86", + 'FIGURE SPACE' => "\xe2\x80\x87", + 'PUNCTUATION SPACE' => "\xe2\x80\x88", + 'THIN SPACE' => "\xe2\x80\x89", + 'HAIR SPACE' => "\xe2\x80\x8a", + 'LINE SEPARATOR' => "\xe2\x80\xa8", + 'PARAGRAPH SEPARATOR' => "\xe2\x80\xa9", + 'ZERO WIDTH SPACE' => "\xe2\x80\x8b", + 'NARROW NO-BREAK SPACE' => "\xe2\x80\xaf", + 'MEDIUM MATHEMATICAL SPACE' => "\xe2\x81\x9f", + 'IDEOGRAPHIC SPACE' => "\xe3\x80\x80", + 'HALFWIDTH HANGUL FILLER' => "\xef\xbe\xa0", + ]; + + /** + * @var array + * + * @phpstan-var array{upper: string[], lower: string[]} + */ + private static $COMMON_CASE_FOLD = [ + 'upper' => [ + 'µ', + 'ſ', + "\xCD\x85", + 'ς', + 'ẞ', + "\xCF\x90", + "\xCF\x91", + "\xCF\x95", + "\xCF\x96", + "\xCF\xB0", + "\xCF\xB1", + "\xCF\xB5", + "\xE1\xBA\x9B", + "\xE1\xBE\xBE", + ], + 'lower' => [ + 'μ', + 's', + 'ι', + 'σ', + 'ß', + 'β', + 'θ', + 'φ', + 'π', + 'κ', + 'ρ', + 'ε', + "\xE1\xB9\xA1", + 'ι', + ], + ]; + + /** + * @var array + * + * @phpstan-var array + */ + private static $SUPPORT = []; + + /** + * @var string[]|null + * + * @phpstan-var array|null + */ + private static $BROKEN_UTF8_FIX; + + /** + * @var string[]|null + * + * @phpstan-var array|null + */ + private static $WIN1252_TO_UTF8; + + /** + * @var string[]|null + * + * @phpstan-var array|null + */ + private static $INTL_TRANSLITERATOR_LIST; + + /** + * @var string[]|null + * + * @phpstan-var array|null + */ + private static $ENCODINGS; + + /** + * @var int[]|null + * + * @phpstan-var array|null + */ + private static $ORD; + + /** + * @var string[]|null + * + * @phpstan-var array|null + */ + private static $EMOJI; + + /** + * @var string[]|null + * + * @phpstan-var array|null + */ + private static $EMOJI_VALUES_CACHE; + + /** + * @var string[]|null + * + * @phpstan-var array|null + */ + private static $EMOJI_KEYS_CACHE; + + /** + * @var string[]|null + * + * @phpstan-var array|null + */ + private static $EMOJI_KEYS_REVERSIBLE_CACHE; + + /** + * @var string[]|null + * + * @phpstan-var array|null + */ + private static $CHR; + + /** + * __construct() + */ + public function __construct() + { + } + + /** + * Return the character at the specified position: $str[1] like functionality. + * + * EXAMPLE: UTF8::access('fòô', 1); // 'ò' + * + * @param string $str

A UTF-8 string.

+ * @param int $pos

The position of character to return.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + *

Single multi-byte character.

+ */ + public static function access(string $str, int $pos, string $encoding = 'UTF-8'): string + { + if ($str === '' || $pos < 0) { + return ''; + } + + if ($encoding === 'UTF-8') { + return (string) \mb_substr($str, $pos, 1); + } + + return (string) self::substr($str, $pos, 1, $encoding); + } + + /** + * Prepends UTF-8 BOM character to the string and returns the whole string. + * + * INFO: If BOM already existed there, the Input string is returned. + * + * EXAMPLE: UTF8::add_bom_to_string('fòô'); // "\xEF\xBB\xBF" . 'fòô' + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return non-empty-string + *

The output string that contains BOM.

+ */ + public static function add_bom_to_string(string $str): string + { + if (!self::string_has_bom($str)) { + $str = self::bom() . $str; + } + + \assert($str !== ''); + + return $str; + } + + /** + * Changes all keys in an array. + * + * @param array $array

The array to work on

+ * @param int $case [optional]

Either CASE_UPPER
+ * or CASE_LOWER (default)

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string[] + *

An array with its keys lower- or uppercased.

+ */ + public static function array_change_key_case( + array $array, + int $case = \CASE_LOWER, + string $encoding = 'UTF-8' + ): array { + if ( + $case !== \CASE_LOWER + && + $case !== \CASE_UPPER + ) { + $case = \CASE_LOWER; + } + + $return = []; + foreach ($array as $key => &$value) { + $key = $case === \CASE_LOWER + ? self::strtolower($key, $encoding) + : self::strtoupper($key, $encoding); + + $return[$key] = $value; + } + + return $return; + } + + /** + * Returns the substring between $start and $end, if found, or an empty + * string. An optional offset may be supplied from which to begin the + * search for the start string. + * + * @param string $str + * @param string $start

Delimiter marking the start of the substring.

+ * @param string $end

Delimiter marking the end of the substring.

+ * @param int $offset [optional]

Index from which to begin the search. Default: 0

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + */ + public static function between( + string $str, + string $start, + string $end, + int $offset = 0, + string $encoding = 'UTF-8' + ): string { + if ($encoding === 'UTF-8') { + $start_position = \mb_strpos($str, $start, $offset); + if ($start_position === false) { + return ''; + } + + $substr_index = $start_position + (int) \mb_strlen($start); + $end_position = \mb_strpos($str, $end, $substr_index); + if ( + $end_position === false + || + $end_position === $substr_index + ) { + return ''; + } + + return (string) \mb_substr($str, $substr_index, $end_position - $substr_index); + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + $start_position = self::strpos($str, $start, $offset, $encoding); + if ($start_position === false) { + return ''; + } + + $substr_index = $start_position + (int) self::strlen($start, $encoding); + $end_position = self::strpos($str, $end, $substr_index, $encoding); + if ( + $end_position === false + || + $end_position === $substr_index + ) { + return ''; + } + + return (string) self::substr( + $str, + $substr_index, + $end_position - $substr_index, + $encoding + ); + } + + /** + * Convert binary into a string. + * + * INFO: opposite to UTF8::str_to_binary() + * + * EXAMPLE: UTF8::binary_to_str('11110000100111111001100010000011'); // '😃' + * + * @param string $bin 1|0 + * + * @psalm-pure + * + * @return string + */ + public static function binary_to_str($bin): string + { + if (!isset($bin[0])) { + return ''; + } + + $convert = \base_convert($bin, 2, 16); + if ($convert === '0') { + return ''; + } + + return \pack('H*', $convert); + } + + /** + * Returns the UTF-8 Byte Order Mark Character. + * + * INFO: take a look at UTF8::$bom for e.g. UTF-16 and UTF-32 BOM values + * + * EXAMPLE: UTF8::bom(); // "\xEF\xBB\xBF" + * + * @psalm-pure + * + * @return non-empty-string + *

UTF-8 Byte Order Mark.

+ */ + public static function bom(): string + { + return "\xef\xbb\xbf"; + } + + /** + * @alias of UTF8::chr_map() + * + * @param callable(string): string $callback + * @param string $str + * + * @psalm-pure + * + * @return string[] + * + * @see UTF8::chr_map() + */ + public static function callback($callback, string $str): array + { + return self::chr_map($callback, $str); + } + + /** + * Returns the character at $index, with indexes starting at 0. + * + * @param string $str

The input string.

+ * @param int<1, max> $index

Position of the character.

+ * @param string $encoding [optional]

Default is UTF-8

+ * + * @psalm-pure + * + * @return string + *

The character at $index.

+ */ + public static function char_at(string $str, int $index, string $encoding = 'UTF-8'): string + { + if ($encoding === 'UTF-8') { + return (string) \mb_substr($str, $index, 1); + } + + return (string) self::substr($str, $index, 1, $encoding); + } + + /** + * Returns an array consisting of the characters in the string. + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return string[] + *

An array of chars.

+ * + * @template T as string + * @phpstan-param T $str + * @phpstan-return (T is non-empty-string ? non-empty-list : list) + */ + public static function chars(string $str): array + { + return self::str_split($str); + } + + /** + * This method will auto-detect your server environment for UTF-8 support. + * + * @return true|null + * + * @internal

You don't need to run it manually, it will be triggered if it's needed.

+ */ + public static function checkForSupport() + { + if (!isset(self::$SUPPORT['already_checked_via_portable_utf8'])) { + self::$SUPPORT['already_checked_via_portable_utf8'] = true; + + // http://php.net/manual/en/book.mbstring.php + self::$SUPPORT['mbstring'] = self::mbstring_loaded(); + + self::$SUPPORT['mbstring_func_overload'] = self::mbstring_overloaded(); + if (self::$SUPPORT['mbstring'] === true) { + \mb_internal_encoding('UTF-8'); + \mb_regex_encoding('UTF-8'); + self::$SUPPORT['mbstring_internal_encoding'] = 'UTF-8'; + } + + // http://php.net/manual/en/book.iconv.php + self::$SUPPORT['iconv'] = self::iconv_loaded(); + + // http://php.net/manual/en/book.intl.php + self::$SUPPORT['intl'] = self::intl_loaded(); + + // http://php.net/manual/en/class.intlchar.php + self::$SUPPORT['intlChar'] = self::intlChar_loaded(); + + // http://php.net/manual/en/book.ctype.php + self::$SUPPORT['ctype'] = self::ctype_loaded(); + + // http://php.net/manual/en/class.finfo.php + self::$SUPPORT['finfo'] = self::finfo_loaded(); + + // http://php.net/manual/en/book.json.php + self::$SUPPORT['json'] = self::json_loaded(); + + // http://php.net/manual/en/book.pcre.php + self::$SUPPORT['pcre_utf8'] = self::pcre_utf8_support(); + + self::$SUPPORT['symfony_polyfill_used'] = self::symfony_polyfill_used(); + if (self::$SUPPORT['symfony_polyfill_used'] === true) { + \mb_internal_encoding('UTF-8'); + self::$SUPPORT['mbstring_internal_encoding'] = 'UTF-8'; + } + + return true; + } + + return null; + } + + /** + * Generates a UTF-8 encoded character from the given code point. + * + * INFO: opposite to UTF8::ord() + * + * EXAMPLE: UTF8::chr(0x2603); // '☃' + * + * @param int $code_point

The code point for which to generate a character.

+ * @param string $encoding [optional]

Default is UTF-8

+ * + * @psalm-pure + * + * @return string|null + *

Multi-byte character, returns null on failure or empty input.

+ */ + public static function chr($code_point, string $encoding = 'UTF-8') + { + // init + /** + * @psalm-suppress ImpureStaticVariable + * + * @var array + */ + static $CHAR_CACHE = []; + + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + if ( + $encoding !== 'UTF-8' + && + $encoding !== 'ISO-8859-1' + && + $encoding !== 'WINDOWS-1252' + && + self::$SUPPORT['mbstring'] === false + ) { + /** + * @psalm-suppress ImpureFunctionCall - this is only a warning + */ + \trigger_error('UTF8::chr() without mbstring cannot handle "' . $encoding . '" encoding', \E_USER_WARNING); + } + + if ( + !\is_int($code_point) /* @phpstan-ignore-line | hack for bad inputs */ + || + $code_point <= 0 + ) { + return null; + } + + $cache_key = $code_point . '_' . $encoding; + if (isset($CHAR_CACHE[$cache_key])) { + return $CHAR_CACHE[$cache_key]; + } + + if ($code_point <= 0x80) { // only for "simple"-chars + + if (self::$CHR === null) { + self::$CHR = self::getData('chr'); + } + + /** + * @psalm-suppress PossiblyNullArrayAccess + */ + $chr = self::$CHR[$code_point]; + + if ($encoding !== 'UTF-8') { + $chr = self::encode($encoding, $chr); + } + + return $CHAR_CACHE[$cache_key] = $chr; + } + + // + // fallback via "IntlChar" + // + + if (self::$SUPPORT['intlChar'] === true) { + $chr = \IntlChar::chr($code_point); + + if ($encoding !== 'UTF-8') { + $chr = self::encode($encoding, $chr); + } + + return $CHAR_CACHE[$cache_key] = $chr; + } + + // + // fallback via vanilla php + // + + if (self::$CHR === null) { + self::$CHR = self::getData('chr'); + } + + $code_point = (int) $code_point; + if ($code_point <= 0x7FF) { + /** + * @psalm-suppress PossiblyNullArrayAccess + */ + $chr = self::$CHR[($code_point >> 6) + 0xC0] . + self::$CHR[($code_point & 0x3F) + 0x80]; + } elseif ($code_point <= 0xFFFF) { + /** + * @psalm-suppress PossiblyNullArrayAccess + */ + $chr = self::$CHR[($code_point >> 12) + 0xE0] . + self::$CHR[(($code_point >> 6) & 0x3F) + 0x80] . + self::$CHR[($code_point & 0x3F) + 0x80]; + } else { + /** + * @psalm-suppress PossiblyNullArrayAccess + */ + $chr = self::$CHR[($code_point >> 18) + 0xF0] . + self::$CHR[(($code_point >> 12) & 0x3F) + 0x80] . + self::$CHR[(($code_point >> 6) & 0x3F) + 0x80] . + self::$CHR[($code_point & 0x3F) + 0x80]; + } + + if ($encoding !== 'UTF-8') { + $chr = self::encode($encoding, $chr); + } + + return $CHAR_CACHE[$cache_key] = $chr; + } + + /** + * Applies callback to all characters of a string. + * + * EXAMPLE: UTF8::chr_map([UTF8::class, 'strtolower'], 'Κόσμε'); // ['κ','ό', 'σ', 'μ', 'ε'] + * + * @param callable(string): string $callback

The callback function.

+ * @param string $str

UTF-8 string to run callback on.

+ * + * @psalm-pure + * + * @return string[] + *

The outcome of the callback, as array.

+ */ + public static function chr_map($callback, string $str): array + { + return \array_map( + $callback, + self::str_split($str) + ); + } + + /** + * Generates an array of byte length of each character of a Unicode string. + * + * 1 byte => U+0000 - U+007F + * 2 byte => U+0080 - U+07FF + * 3 byte => U+0800 - U+FFFF + * 4 byte => U+10000 - U+10FFFF + * + * EXAMPLE: UTF8::chr_size_list('中文空白-test'); // [3, 3, 3, 3, 1, 1, 1, 1, 1] + * + * @param string $str

The original unicode string.

+ * + * @psalm-pure + * + * @return int[] + *

An array of byte lengths of each character.

+ * + * @template T as string + * @phpstan-param T $str + * @phpstan-return (T is non-empty-string ? non-empty-list<1|2|3|4> : list<1|2|3|4>) + */ + public static function chr_size_list(string $str): array + { + if ($str === '') { + return []; + } + + if (self::$SUPPORT['mbstring_func_overload'] === true) { + /* @phpstan-ignore-next-line | str_split only give one char, so that we only got int<1,4> */ + return \array_map( + static function (string $data): int { + // "mb_" is available if overload is used, so use it ... + return \mb_strlen($data, 'CP850'); // 8-BIT + }, + self::str_split($str) + ); + } + + /* @phpstan-ignore-next-line | str_split only give one char, so that we only got int<1,4> */ + return \array_map('\strlen', self::str_split($str)); + } + + /** + * Get a decimal code representation of a specific character. + * + * INFO: opposite to UTF8::decimal_to_chr() + * + * EXAMPLE: UTF8::chr_to_decimal('§'); // 0xa7 + * + * @param string $char

The input character.

+ * + * @psalm-pure + * + * @return int + */ + public static function chr_to_decimal(string $char): int + { + if (self::$SUPPORT['iconv'] === true) { + $chr_tmp = \iconv('UTF-8', 'UCS-4LE', $char); + if ($chr_tmp !== false) { + /** @phpstan-ignore-next-line - "unpack": only false if the format string contains errors */ + return \unpack('V', $chr_tmp)[1]; + } + } + + $code = self::ord($char[0]); + $bytes = 1; + + if (!($code & 0x80)) { + // 0xxxxxxx + return $code; + } + + if (($code & 0xe0) === 0xc0) { + // 110xxxxx + $bytes = 2; + $code &= ~0xc0; + } elseif (($code & 0xf0) === 0xe0) { + // 1110xxxx + $bytes = 3; + $code &= ~0xe0; + } elseif (($code & 0xf8) === 0xf0) { + // 11110xxx + $bytes = 4; + $code &= ~0xf0; + } + + for ($i = 2; $i <= $bytes; ++$i) { + // 10xxxxxx + $code = ($code << 6) + (self::ord($char[$i - 1]) & ~0x80); + } + + return $code; + } + + /** + * Get hexadecimal code point (U+xxxx) of a UTF-8 encoded character. + * + * EXAMPLE: UTF8::chr_to_hex('§'); // U+00a7 + * + * @param int|string $char

The input character

+ * @param string $prefix [optional] + * + * @psalm-pure + * + * @return string + *

The code point encoded as U+xxxx.

+ */ + public static function chr_to_hex($char, string $prefix = 'U+'): string + { + if ($char === '') { + return ''; + } + + if ($char === '�') { + $char = ''; + } + + return self::int_to_hex(self::ord((string) $char), $prefix); + } + + /** + * Splits a string into smaller chunks and multiple lines, using the specified line ending character. + * + * EXAMPLE: UTF8::chunk_split('ABC-ÖÄÜ-中文空白-κόσμε', 3); // "ABC\r\n-ÖÄ\r\nÜ-中\r\n文空白\r\n-κό\r\nσμε" + * + * @param string $str

The original string to be split.

+ * @param int<1, max> $chunk_length [optional]

The maximum character length of a chunk.

+ * @param string $end [optional]

The character(s) to be inserted at the end of each chunk.

+ * + * @psalm-pure + * + * @return string + *

The chunked string.

+ * + * @template T as string + * @phpstan-param T $str + * @phpstan-return (T is non-empty-string ? non-empty-string : string) + */ + public static function chunk_split(string $str, int $chunk_length = 76, string $end = "\r\n"): string + { + return \implode($end, self::str_split($str, $chunk_length)); + } + + /** + * Accepts a string and removes all non-UTF-8 characters from it + extras if needed. + * + * EXAMPLE: UTF8::clean("\xEF\xBB\xBF„Abcdef\xc2\xa0\x20…” — 😃 - Düsseldorf", true, true); // '„Abcdef …” — 😃 - Düsseldorf' + * + * @param string $str

The string to be sanitized.

+ * @param bool $remove_bom [optional]

Set to true, if you need to remove + * UTF-BOM.

+ * @param bool $normalize_whitespace [optional]

Set to true, if you need to normalize the + * whitespace.

+ * @param bool $normalize_msword [optional]

Set to true, if you need to normalize MS + * Word chars e.g.: "…" + * => "..."

+ * @param bool $keep_non_breaking_space [optional]

Set to true, to keep non-breaking-spaces, + * in + * combination with + * $normalize_whitespace

+ * @param bool $replace_diamond_question_mark [optional]

Set to true, if you need to remove diamond + * question mark e.g.: "�"

+ * @param bool $remove_invisible_characters [optional]

Set to false, if you not want to remove + * invisible characters e.g.: "\0"

+ * @param bool $remove_invisible_characters_url_encoded [optional]

Set to true, if you not want to remove + * invisible url encoded characters e.g.: "%0B"
WARNING: + * maybe contains false-positives e.g. aa%0Baa -> aaaa. + *

+ * + * @psalm-pure + * + * @return string + *

An clean UTF-8 encoded string.

+ */ + public static function clean( + string $str, + bool $remove_bom = false, + bool $normalize_whitespace = false, + bool $normalize_msword = false, + bool $keep_non_breaking_space = false, + bool $replace_diamond_question_mark = false, + bool $remove_invisible_characters = true, + bool $remove_invisible_characters_url_encoded = false + ): string { + // http://stackoverflow.com/questions/1401317/remove-non-utf8-characters-from-string + // caused connection reset problem on larger strings + + $regex = '/ + ( + (?: [\x00-\x7F] # single-byte sequences 0xxxxxxx + | [\xC0-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx + | [\xE0-\xEF][\x80-\xBF]{2} # triple-byte sequences 1110xxxx 10xxxxxx * 2 + | [\xF0-\xF7][\x80-\xBF]{3} # quadruple-byte sequence 11110xxx 10xxxxxx * 3 + ){1,100} # ...one or more times + ) + | ( [\x80-\xBF] ) # invalid byte in range 10000000 - 10111111 + | ( [\xC0-\xFF] ) # invalid byte in range 11000000 - 11111111 + /x'; + $str = (string) \preg_replace($regex, '$1', $str); + + if ($replace_diamond_question_mark) { + $str = self::replace_diamond_question_mark($str); + } + + if ($remove_invisible_characters) { + $str = self::remove_invisible_characters($str, $remove_invisible_characters_url_encoded); + } + + if ($normalize_whitespace) { + $str = self::normalize_whitespace($str, $keep_non_breaking_space); + } + + if ($normalize_msword) { + $str = self::normalize_msword($str); + } + + if ($remove_bom) { + $str = self::remove_bom($str); + } + + return $str; + } + + /** + * Clean-up a string and show only printable UTF-8 chars at the end + fix UTF-8 encoding. + * + * EXAMPLE: UTF8::cleanup("\xEF\xBB\xBF„Abcdef\xc2\xa0\x20…” — 😃 - Düsseldorf", true, true); // '„Abcdef …” — 😃 - Düsseldorf' + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return string + */ + public static function cleanup($str): string + { + // init + $str = (string) $str; + + if ($str === '') { + return ''; + } + + // fixed ISO <-> UTF-8 Errors + $str = self::fix_simple_utf8($str); + + // remove all none UTF-8 symbols + // && remove diamond question mark (�) + // && remove remove invisible characters (e.g. "\0") + // && remove BOM + // && normalize whitespace chars (but keep non-breaking-spaces) + return self::clean( + $str, + true, + true, + false, + true, + true + ); + } + + /** + * Accepts a string or an array of chars and returns an array of Unicode code points. + * + * INFO: opposite to UTF8::string() + * + * EXAMPLE: + * UTF8::codepoints('κöñ'); // array(954, 246, 241) + * // ... OR ... + * UTF8::codepoints('κöñ', true); // array('U+03ba', 'U+00f6', 'U+00f1') + * + * + * @param string|string[] $arg

A UTF-8 encoded string or an array of such chars.

+ * @param bool $use_u_style

If True, will return code points in U+xxxx format, + * default, code points will be returned as integers.

+ * + * @psalm-pure + * + * @return int[]|string[] + *

+ * The array of code points:
+ * int[] for $u_style === false
+ * string[] for $u_style === true
+ *

+ * + * @template T as string|string[] + * @phpstan-param T $arg + * @phpstan-return (T is non-empty-string ? ($use_u_style is true ? non-empty-list : non-empty-list) : ($use_u_style is true ? list : list)) + */ + public static function codepoints($arg, bool $use_u_style = false): array + { + if (\is_string($arg)) { + $arg = self::str_split($arg); + } + + /** + * @psalm-suppress DocblockTypeContradiction + * @phpstan-ignore-next-line hack for bad inputs + */ + if (!\is_array($arg)) { + return []; + } + + if ($arg === []) { + return []; + } + + $arg = \array_map( + [ + self::class, + 'ord', + ], + $arg + ); + + if ($use_u_style) { + $arg = \array_map( + [ + self::class, + 'int_to_hex', + ], + $arg + ); + } + + /* @phpstan-ignore-next-line | FP? */ + return $arg; + } + + /** + * Trims the string and replaces consecutive whitespace characters with a + * single space. This includes tabs and newline characters, as well as + * multibyte whitespace such as the thin space and ideographic space. + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return string + *

A string with trimmed $str and condensed whitespace.

+ */ + public static function collapse_whitespace(string $str): string + { + if (self::$SUPPORT['mbstring'] === true) { + return \trim((string) \mb_ereg_replace('[[:space:]]+', ' ', $str)); + } + + return \trim(self::regex_replace($str, '[[:space:]]+', ' ')); + } + + /** + * Returns count of characters used in a string. + * + * EXAMPLE: UTF8::count_chars('κaκbκc'); // array('κ' => 3, 'a' => 1, 'b' => 1, 'c' => 1) + * + * @param string $str

The input string.

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * @param bool $try_to_use_mb_functions [optional]

Set to false, if you don't want to use + * + * @psalm-pure + * + * @return int[] + *

An associative array of Character as keys and + * their count as values.

+ * + * @template T as string + * @phpstan-param T $str + * @phpstan-return (T is non-empty-string ? non-empty-array : array) + */ + public static function count_chars( + string $str, + bool $clean_utf8 = false, + bool $try_to_use_mb_functions = true + ): array { + return \array_count_values( + self::str_split( + $str, + 1, + $clean_utf8, + $try_to_use_mb_functions + ) + ); + } + + /** + * Create a valid CSS identifier for e.g. "class"- or "id"-attributes. + * + * EXAMPLE: UTF8::css_identifier('123foo/bar!!!'); // _23foo-bar + * + * copy&past from https://github.com/drupal/core/blob/8.8.x/lib/Drupal/Component/Utility/Html.php#L95 + * + * @param string $str

INFO: if no identifier is given e.g. " " or "", we will create a unique string automatically

+ * @param string[] $filter + * @param bool $strip_tags + * @param bool $strtolower + * + * @psalm-pure + * + * @return string + * + * @phpstan-param array $filter + */ + public static function css_identifier( + string $str = '', + array $filter = [ + ' ' => '-', + '/' => '-', + '[' => '', + ']' => '', + ], + bool $strip_tags = false, + bool $strtolower = true + ): string { + // We could also use strtr() here but its much slower than str_replace(). In + // order to keep '__' to stay '__' we first replace it with a different + // placeholder after checking that it is not defined as a filter. + $double_underscore_replacements = 0; + + $str = \trim($str); + if ($str) { + $str = self::clean($str, true); + } + + if ($strip_tags) { + $str = \strip_tags($str); + } + + $str = \trim($str); + // fallback (1) + if (!$str) { + $str = \uniqid('auto-generated-css-class', true); + } + + if ($strtolower) { + $str = \strtolower($str); + } + + if (!isset($filter['__'])) { + $str = \str_replace('__', '##', $str, $double_underscore_replacements); + } + + $str = \str_replace(\array_keys($filter), \array_values($filter), $str); + // Replace temporary placeholder '##' with '__' only if the original + // $identifier contained '__'. + if ($double_underscore_replacements > 0) { + $str = \str_replace('##', '__', $str); + } + + // Valid characters in a CSS identifier are: + // - the hyphen (U+002D) + // - a-z (U+0030 - U+0039) + // - A-Z (U+0041 - U+005A) + // - the underscore (U+005F) + // - 0-9 (U+0061 - U+007A) + // - ISO 10646 characters U+00A1 and higher + // We strip out any character not in the above list. + $str = (string) \preg_replace('/[^\x{002D}\x{0030}-\x{0039}\x{0041}-\x{005A}\x{005F}\x{0061}-\x{007A}\x{00A1}-\x{FFFF}]/u', '', $str); + // Identifiers cannot start with a digit, two hyphens, or a hyphen followed by a digit. + $str = (string) \preg_replace(['/^[0-9]/', '/^(-[0-9])|^(--)/'], ['_', '__'], $str); + + return \trim($str, '-'); + } + + /** + * Remove css media-queries. + * + * @param string $str + * + * @psalm-pure + * + * @return string + */ + public static function css_stripe_media_queries(string $str): string + { + return (string) \preg_replace( + '#@media\\s+(?:only\\s)?(?:[\\s{(]|screen|all)\\s?[^{]+{.*}\\s*}\\s*#isumU', + '', + $str + ); + } + + /** + * Checks whether ctype is available on the server. + * + * @psalm-pure + * + * @return bool + *

true if available, false otherwise

+ * + * @internal

Please do not use it anymore, we will make is private in next major version.

+ */ + public static function ctype_loaded(): bool + { + return \extension_loaded('ctype'); + } + + /** + * Converts an int value into a UTF-8 character. + * + * INFO: opposite to UTF8::string() + * + * EXAMPLE: UTF8::decimal_to_chr(931); // 'Σ' + * + * @param int|string $int + * + * @phpstan-param int|numeric-string $int + * + * @psalm-pure + * + * @return string + */ + public static function decimal_to_chr($int): string + { + // We cannot use html_entity_decode() here, as it will not return + // characters for many values < 160. + return \mb_convert_encoding('&#' . $int . ';', 'UTF-8', 'HTML-ENTITIES'); + } + + /** + * Decodes a MIME header field + * + * @param string $str + * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return false|string + *

A decoded MIME field on success, + * or false if an error occurs during the decoding.

+ */ + public static function decode_mimeheader($str, string $encoding = 'UTF-8') + { + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + // always fallback via symfony polyfill + return \iconv_mime_decode($str, \ICONV_MIME_DECODE_CONTINUE_ON_ERROR, $encoding); + } + + /** + * Convert any two-letter country code (ISO 3166-1) to the corresponding Emoji. + * + * @see https://en.wikipedia.org/wiki/ISO_3166-1 + * + * @param string $country_code_iso_3166_1

e.g. DE

+ * + * @return string + *

Emoji or empty string on error.

+ */ + public static function emoji_from_country_code(string $country_code_iso_3166_1): string + { + if ($country_code_iso_3166_1 === '') { + return ''; + } + + if (self::strlen($country_code_iso_3166_1) !== 2) { + return ''; + } + + $country_code_iso_3166_1 = \strtoupper($country_code_iso_3166_1); + + $flagOffset = 0x1F1E6; + $asciiOffset = 0x41; + + return (self::chr((self::ord($country_code_iso_3166_1[0]) - $asciiOffset + $flagOffset)) ?? '') . + (self::chr((self::ord($country_code_iso_3166_1[1]) - $asciiOffset + $flagOffset)) ?? ''); + } + + /** + * Decodes a string which was encoded by "UTF8::emoji_encode()". + * + * INFO: opposite to UTF8::emoji_encode() + * + * EXAMPLE: + * UTF8::emoji_decode('foo CHARACTER_OGRE', false); // 'foo 👹' + * // + * UTF8::emoji_decode('foo _-_PORTABLE_UTF8_-_308095726_-_627590803_-_8FTU_ELBATROP_-_', true); // 'foo 👹' + * + * + * @param string $str

The input string.

+ * @param bool $use_reversible_string_mappings [optional]

+ * When TRUE, we se a reversible string mapping + * between "emoji_encode" and "emoji_decode".

+ * + * @psalm-pure + * + * @return string + */ + public static function emoji_decode( + string $str, + bool $use_reversible_string_mappings = false + ): string { + if (self::$EMOJI_KEYS_CACHE === null) { + /** @phpstan-ignore-next-line - we need to load the data first */ + self::initEmojiData(); + } + + if ($use_reversible_string_mappings) { + return (string) \str_replace( + (array) self::$EMOJI_KEYS_REVERSIBLE_CACHE, + (array) self::$EMOJI_VALUES_CACHE, + $str + ); + } + + return (string) \str_replace( + (array) self::$EMOJI_KEYS_CACHE, + (array) self::$EMOJI_VALUES_CACHE, + $str + ); + } + + /** + * Encode a string with emoji chars into a non-emoji string. + * + * INFO: opposite to UTF8::emoji_decode() + * + * EXAMPLE: + * UTF8::emoji_encode('foo 👹', false)); // 'foo CHARACTER_OGRE' + * // + * UTF8::emoji_encode('foo 👹', true)); // 'foo _-_PORTABLE_UTF8_-_308095726_-_627590803_-_8FTU_ELBATROP_-_' + * + * + * @param string $str

The input string

+ * @param bool $use_reversible_string_mappings [optional]

+ * when TRUE, we use a reversible string mapping + * between "emoji_encode" and "emoji_decode"

+ * + * @psalm-pure + * + * @return string + */ + public static function emoji_encode( + string $str, + bool $use_reversible_string_mappings = false + ): string { + if (self::$EMOJI_KEYS_CACHE === null) { + /** @phpstan-ignore-next-line - we need to load the data first */ + self::initEmojiData(); + } + + if ($use_reversible_string_mappings) { + return (string) \str_replace( + (array) self::$EMOJI_VALUES_CACHE, + (array) self::$EMOJI_KEYS_REVERSIBLE_CACHE, + $str + ); + } + + return (string) \str_replace( + (array) self::$EMOJI_VALUES_CACHE, + (array) self::$EMOJI_KEYS_CACHE, + $str + ); + } + + /** + * Encode a string with a new charset-encoding. + * + * INFO: This function will also try to fix broken / double encoding, + * so you can call this function also on a UTF-8 string and you don't mess up the string. + * + * EXAMPLE: + * UTF8::encode('ISO-8859-1', '-ABC-中文空白-'); // '-ABC-????-' + * // + * UTF8::encode('UTF-8', '-ABC-中文空白-'); // '-ABC-中文空白-' + * // + * UTF8::encode('HTML', '-ABC-中文空白-'); // '-ABC-中文空白-' + * // + * UTF8::encode('BASE64', '-ABC-中文空白-'); // 'LUFCQy3kuK3mlofnqbrnmb0t' + * + * + * @param string $to_encoding

e.g. 'UTF-16', 'UTF-8', 'ISO-8859-1', etc.

+ * @param string $str

The input string

+ * @param bool $auto_detect_the_from_encoding [optional]

Force the new encoding (we try to fix broken / double + * encoding for UTF-8)
otherwise we auto-detect the current + * string-encoding

+ * @param string $from_encoding [optional]

e.g. 'UTF-16', 'UTF-8', 'ISO-8859-1', etc.
+ * A empty string will trigger the autodetect anyway.

+ * + * @psalm-pure + * + * @return string + * + * @psalm-suppress InvalidReturnStatement + */ + public static function encode( + string $to_encoding, + string $str, + bool $auto_detect_the_from_encoding = true, + string $from_encoding = '' + ): string { + if ($str === '' || $to_encoding === '') { + return $str; + } + + if ($to_encoding !== 'UTF-8' && $to_encoding !== 'CP850') { + $to_encoding = self::normalize_encoding($to_encoding, 'UTF-8'); + } + + if ($from_encoding && $from_encoding !== 'UTF-8' && $from_encoding !== 'CP850') { + $from_encoding = self::normalize_encoding($from_encoding); + } + + if ( + $to_encoding + && + $from_encoding + && + $from_encoding === $to_encoding + ) { + return $str; + } + + if ($from_encoding === 'JSON') { + $str = self::json_decode($str); + $from_encoding = ''; + } + + if ($from_encoding === 'BASE64') { + $str = \base64_decode($str, true); + $from_encoding = ''; + } + + if ($from_encoding === 'HTML-ENTITIES') { + /* @phpstan-ignore-next-line | $str has manybe changed */ + $str = self::html_entity_decode($str, \ENT_COMPAT); + $from_encoding = ''; + } + + if ($to_encoding === 'JSON') { + $return = self::json_encode($str); + if ($return === false) { + throw new \InvalidArgumentException('The input string [' . $str . '] can not be used for json_encode().'); + } + + return $return; + } + + if ($to_encoding === 'BASE64') { + return \base64_encode($str); + } + + if ($to_encoding === 'HTML-ENTITIES') { + /* @phpstan-ignore-next-line | $str has manybe changed */ + return self::html_encode($str, true); + } + + if ( + $auto_detect_the_from_encoding + || + !$from_encoding + ) { + $from_encoding_auto_detected = self::str_detect_encoding($str); + } else { + $from_encoding_auto_detected = false; + } + + // DEBUG + //var_dump($to_encoding, $from_encoding, $from_encoding_auto_detected, $str, "\n\n"); + + if ($from_encoding_auto_detected !== false) { + $from_encoding = $from_encoding_auto_detected; + } elseif ($auto_detect_the_from_encoding) { + // fallback for the "autodetect"-mode + /* @phpstan-ignore-next-line | $str has manybe changed */ + return self::to_utf8($str); + } + + if ( + !$from_encoding + || + $from_encoding === $to_encoding + ) { + return $str; + } + + if ( + $to_encoding === 'UTF-8' + && + ( + $from_encoding === 'WINDOWS-1252' + || + $from_encoding === 'ISO-8859-1' + ) + ) { + /* @phpstan-ignore-next-line | $str has manybe changed */ + return self::to_utf8($str); + } + + if ( + $to_encoding === 'ISO-8859-1' + && + ( + $from_encoding === 'WINDOWS-1252' + || + $from_encoding === 'UTF-8' + ) + ) { + /* @phpstan-ignore-next-line | $str has manybe changed */ + return self::to_iso8859($str); + } + + if ( + $to_encoding !== 'UTF-8' + && + $to_encoding !== 'ISO-8859-1' + && + $to_encoding !== 'WINDOWS-1252' + && + self::$SUPPORT['mbstring'] === false + ) { + /** + * @psalm-suppress ImpureFunctionCall - this is only a warning + */ + \trigger_error('UTF8::encode() without mbstring cannot handle "' . $to_encoding . '" encoding', \E_USER_WARNING); + } + + if (self::$SUPPORT['mbstring'] === true) { + $str_encoded = \mb_convert_encoding( + $str, + $to_encoding, + $from_encoding + ); + + if ($str_encoded) { + \assert(\is_string($str_encoded)); + + return $str_encoded; + } + } + + /** @noinspection PhpUsageOfSilenceOperatorInspection - Detected an incomplete multibyte character in input string */ + $return = @\iconv($from_encoding, $to_encoding, $str); + if ($return !== false) { + return $return; + } + + return $str; + } + + /** + * @param string $str + * @param string $from_charset [optional]

Set the input charset.

+ * @param string $to_charset [optional]

Set the output charset.

+ * @param string $transfer_encoding [optional]

Set the transfer encoding.

+ * @param string $linefeed [optional]

Set the used linefeed.

+ * @param int<1, max> $indent [optional]

Set the max length indent.

+ * + * @psalm-pure + * + * @return false|string + *

An encoded MIME field on success, + * or false if an error occurs during the encoding.

+ */ + public static function encode_mimeheader( + string $str, + string $from_charset = 'UTF-8', + string $to_charset = 'UTF-8', + string $transfer_encoding = 'Q', + string $linefeed = "\r\n", + int $indent = 76 + ) { + if ($from_charset !== 'UTF-8' && $from_charset !== 'CP850') { + $from_charset = self::normalize_encoding($from_charset, 'UTF-8'); + } + + if ($to_charset !== 'UTF-8' && $to_charset !== 'CP850') { + $to_charset = self::normalize_encoding($to_charset, 'UTF-8'); + } + + // always fallback via symfony polyfill + return \iconv_mime_encode( + '', + $str, + [ + 'scheme' => $transfer_encoding, + 'line-length' => $indent, + 'input-charset' => $from_charset, + 'output-charset' => $to_charset, + 'line-break-chars' => $linefeed, + ] + ); + } + + /** + * Create an extract from a sentence, so if the search-string was found, it tries to center in the output. + * + * @param string $str

The input string.

+ * @param string $search

The searched string.

+ * @param int|null $length [optional]

Default: null === text->length / 2

+ * @param string $replacer_for_skipped_text [optional]

Default: …

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + */ + public static function extract_text( + string $str, + string $search = '', + int $length = null, + string $replacer_for_skipped_text = '…', + string $encoding = 'UTF-8' + ): string { + if ($str === '') { + return ''; + } + + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + $trim_chars = "\t\r\n -_()!~?=+/*\\,.:;\"'[]{}`&"; + + if ($length === null) { + $length = (int) \round((int) self::strlen($str, $encoding) / 2); + } + + if ($search === '') { + if ($encoding === 'UTF-8') { + if ($length > 0) { + $string_length = (int) \mb_strlen($str); + $end = ($length - 1) > $string_length ? $string_length : ($length - 1); + } else { + $end = 0; + } + + $pos = (int) \min( + \mb_strpos($str, ' ', $end), + \mb_strpos($str, '.', $end) + ); + } else { + if ($length > 0) { + $string_length = (int) self::strlen($str, $encoding); + $end = ($length - 1) > $string_length ? $string_length : ($length - 1); + } else { + $end = 0; + } + + $pos = (int) \min( + self::strpos($str, ' ', $end, $encoding), + self::strpos($str, '.', $end, $encoding) + ); + } + + if ($pos) { + if ($encoding === 'UTF-8') { + $str_sub = \mb_substr($str, 0, $pos); + } else { + $str_sub = self::substr($str, 0, $pos, $encoding); + } + + if ($str_sub === false) { + return ''; + } + + return \rtrim($str_sub, $trim_chars) . $replacer_for_skipped_text; + } + + return $str; + } + + if ($encoding === 'UTF-8') { + $word_position = (int) \mb_stripos($str, $search); + $half_side = (int) ($word_position - $length / 2 + (int) \mb_strlen($search) / 2); + } else { + $word_position = (int) self::stripos($str, $search, 0, $encoding); + $half_side = (int) ($word_position - $length / 2 + (int) self::strlen($search, $encoding) / 2); + } + + $pos_start = 0; + if ($half_side > 0) { + if ($encoding === 'UTF-8') { + $half_text = \mb_substr($str, 0, $half_side); + } else { + $half_text = self::substr($str, 0, $half_side, $encoding); + } + if ($half_text !== false) { + if ($encoding === 'UTF-8') { + $pos_start = (int) \max( + \mb_strrpos($half_text, ' '), + \mb_strrpos($half_text, '.') + ); + } else { + $pos_start = (int) \max( + self::strrpos($half_text, ' ', 0, $encoding), + self::strrpos($half_text, '.', 0, $encoding) + ); + } + } + } + + if ($word_position && $half_side > 0) { + $offset = $pos_start + $length - 1; + $real_length = (int) self::strlen($str, $encoding); + + if ($offset > $real_length) { + $offset = $real_length; + } + + if ($encoding === 'UTF-8') { + $pos_end = (int) \min( + \mb_strpos($str, ' ', $offset), + \mb_strpos($str, '.', $offset) + ) - $pos_start; + } else { + $pos_end = (int) \min( + self::strpos($str, ' ', $offset, $encoding), + self::strpos($str, '.', $offset, $encoding) + ) - $pos_start; + } + + if (!$pos_end || $pos_end <= 0) { + if ($encoding === 'UTF-8') { + $str_sub = \mb_substr($str, $pos_start, (int) \mb_strlen($str)); + } else { + $str_sub = self::substr($str, $pos_start, (int) self::strlen($str, $encoding), $encoding); + } + if ($str_sub !== false) { + $extract = $replacer_for_skipped_text . \ltrim($str_sub, $trim_chars); + } else { + $extract = ''; + } + } else { + if ($encoding === 'UTF-8') { + $str_sub = \mb_substr($str, $pos_start, $pos_end); + } else { + $str_sub = self::substr($str, $pos_start, $pos_end, $encoding); + } + if ($str_sub !== false) { + $extract = $replacer_for_skipped_text . \trim($str_sub, $trim_chars) . $replacer_for_skipped_text; + } else { + $extract = ''; + } + } + } else { + $offset = $length - 1; + $true_length = (int) self::strlen($str, $encoding); + + if ($offset > $true_length) { + $offset = $true_length; + } + + if ($encoding === 'UTF-8') { + $pos_end = (int) \min( + \mb_strpos($str, ' ', $offset), + \mb_strpos($str, '.', $offset) + ); + } else { + $pos_end = (int) \min( + self::strpos($str, ' ', $offset, $encoding), + self::strpos($str, '.', $offset, $encoding) + ); + } + + if ($pos_end) { + if ($encoding === 'UTF-8') { + $str_sub = \mb_substr($str, 0, $pos_end); + } else { + $str_sub = self::substr($str, 0, $pos_end, $encoding); + } + if ($str_sub !== false) { + $extract = \rtrim($str_sub, $trim_chars) . $replacer_for_skipped_text; + } else { + $extract = ''; + } + } else { + $extract = $str; + } + } + + return $extract; + } + + /** + * Reads entire file into a string. + * + * EXAMPLE: UTF8::file_get_contents('utf16le.txt'); // ... + * + * WARNING: Do not use UTF-8 Option ($convert_to_utf8) for binary files (e.g.: images) !!! + * + * @see http://php.net/manual/en/function.file-get-contents.php + * + * @param string $filename

+ * Name of the file to read. + *

+ * @param bool $use_include_path [optional]

+ * Prior to PHP 5, this parameter is called + * use_include_path and is a bool. + * As of PHP 5 the FILE_USE_INCLUDE_PATH can be used + * to trigger include path + * search. + *

+ * @param resource|null $context [optional]

+ * A valid context resource created with + * stream_context_create. If you don't need to use a + * custom context, you can skip this parameter by &null;. + *

+ * @param int|null $offset [optional]

+ * The offset where the reading starts. + *

+ * @param int<0, max>|null $max_length [optional]

+ * Maximum length of data read. The default is to read until end + * of file is reached. + *

+ * @param int $timeout

The time in seconds for the timeout.

+ * @param bool $convert_to_utf8 WARNING!!!

Maybe you can't use this option for + * some files, because they used non default utf-8 chars. Binary files + * like images or pdf will not be converted.

+ * @param string $from_encoding [optional]

e.g. 'UTF-16', 'UTF-8', 'ISO-8859-1', etc.
+ * A empty string will trigger the autodetect anyway.

+ * + * @psalm-pure + * + * @return false|string + *

The function returns the read data as string or false on failure.

+ */ + public static function file_get_contents( + string $filename, + bool $use_include_path = false, + $context = null, + int $offset = null, + int $max_length = null, + int $timeout = 10, + bool $convert_to_utf8 = true, + string $from_encoding = '' + ) { + // init + /** @noinspection CallableParameterUseCaseInTypeContextInspection - is ok here */ + $filename = Bootup::filter_sanitize_string_polyfill($filename); + if ($filename === false) { + return false; + } + + if ($timeout && $context === null) { + $context = \stream_context_create( + [ + 'http' => [ + 'timeout' => $timeout, + ], + ] + ); + } + + if ($offset === null) { + $offset = 0; + } + + if (\is_int($max_length)) { + /* @phpstan-ignore-next-line | we do not trust the phpdoc check */ + if ($max_length < 0) { + $max_length = 0; + } + + $data = \file_get_contents($filename, $use_include_path, $context, $offset, $max_length); + } else { + $data = \file_get_contents($filename, $use_include_path, $context, $offset); + } + + // return false on error + if ($data === false) { + return false; + } + + if ( + $convert_to_utf8 + && + ( + !self::is_binary($data, true) + || + self::is_utf16($data, false) !== false + || + self::is_utf32($data, false) !== false + ) + ) { + $data = self::encode('UTF-8', $data, false, $from_encoding); + $data = self::cleanup($data); + } + + return $data; + } + + /** + * Checks if a file starts with BOM (Byte Order Mark) character. + * + * EXAMPLE: UTF8::file_has_bom('utf8_with_bom.txt'); // true + * + * @param string $file_path

Path to a valid file.

+ * + * @throws \RuntimeException if file_get_contents() returned false + * + * @return bool + *

true if the file has BOM at the start, false otherwise

+ * + * @psalm-pure + */ + public static function file_has_bom(string $file_path): bool + { + $file_content = \file_get_contents($file_path); + if ($file_content === false) { + throw new \RuntimeException('file_get_contents() returned false for:' . $file_path); + } + + return self::string_has_bom($file_content); + } + + /** + * Normalizes to UTF-8 NFC, converting from WINDOWS-1252 when needed. + * + * EXAMPLE: UTF8::filter(array("\xE9", 'à', 'a')); // array('é', 'à', 'a') + * + * @param array|object|string $var + * @param int $normalization_form + * @param string $leading_combining + * + * @psalm-pure + * + * @return mixed + * + * @template TFilter + * @phpstan-param TFilter $var + * @phpstan-return TFilter + */ + public static function filter( + $var, + int $normalization_form = \Normalizer::NFC, + string $leading_combining = '◌' + ) { + switch (\gettype($var)) { + case 'object': + case 'array': + /* @phpstan-ignore-next-line | object & array are both iterable */ + foreach ($var as &$v) { + $v = self::filter($v, $normalization_form, $leading_combining); + } + unset($v); + + break; + case 'string': + + if (\strpos($var, "\r") !== false) { + $var = self::normalize_line_ending($var); + } + + if (!ASCII::is_ascii($var)) { + if (\Normalizer::isNormalized($var, $normalization_form)) { + $n = '-'; + } else { + $n = \Normalizer::normalize($var, $normalization_form); + + if ($n && isset($n[0])) { + $var = $n; + } else { + $var = self::encode('UTF-8', $var); + } + } + + \assert(\is_string($var)); + if ( + $n + && + $var[0] >= "\x80" + && + isset($n[0], $leading_combining[0]) + && + \preg_match('/^\\p{Mn}/u', $var) + ) { + // Prevent leading combining chars + // for NFC-safe concatenations. + $var = $leading_combining . $var; + } + } + + break; + default: + // nothing + } + + /** @noinspection PhpSillyAssignmentInspection */ + /** @phpstan-var TFilter $var */ + $var = $var; + + return $var; + } + + /** + * "filter_input()"-wrapper with normalizes to UTF-8 NFC, converting from WINDOWS-1252 when needed. + * + * Gets a specific external variable by name and optionally filters it. + * + * EXAMPLE: + * // _GET['foo'] = 'bar'; + * UTF8::filter_input(INPUT_GET, 'foo', FILTER_UNSAFE_RAW)); // 'bar' + * + * + * @see http://php.net/manual/en/function.filter-input.php + * + * @param int $type

+ * One of INPUT_GET, INPUT_POST, + * INPUT_COOKIE, INPUT_SERVER, or + * INPUT_ENV. + *

+ * @param string $variable_name

+ * Name of a variable to get. + *

+ * @param int $filter [optional]

+ * The ID of the filter to apply. The + * manual page lists the available filters. + *

+ * @param int|int[]|null $options [optional]

+ * Associative array of options or bitwise disjunction of flags. If filter + * accepts options, flags can be provided in "flags" field of array. + *

+ * + * @psalm-pure + * + * @return mixed + *

+ * Value of the requested variable on success, FALSE if the filter fails, or NULL if the + * variable_name variable is not set. If the flag FILTER_NULL_ON_FAILURE is used, it + * returns FALSE if the variable is not set and NULL if the filter fails. + *

+ */ + public static function filter_input( + int $type, + string $variable_name, + int $filter = \FILTER_DEFAULT, + $options = null + ) { + /** + * @psalm-suppress ImpureFunctionCall - we use func_num_args only for args count matching here + */ + if ($options === null || \func_num_args() < 4) { + $var = \filter_input($type, $variable_name, $filter); + } else { + $var = \filter_input($type, $variable_name, $filter, $options); + } + + return self::filter($var); + } + + /** + * "filter_input_array()"-wrapper with normalizes to UTF-8 NFC, converting from WINDOWS-1252 when needed. + * + * Gets external variables and optionally filters them. + * + * EXAMPLE: + * // _GET['foo'] = 'bar'; + * UTF8::filter_input_array(INPUT_GET, array('foo' => 'FILTER_UNSAFE_RAW')); // array('bar') + * + * + * @see http://php.net/manual/en/function.filter-input-array.php + * + * @param int $type

+ * One of INPUT_GET, INPUT_POST, + * INPUT_COOKIE, INPUT_SERVER, or + * INPUT_ENV. + *

+ * @param array|null $definition [optional]

+ * An array defining the arguments. A valid key is a string + * containing a variable name and a valid value is either a filter type, or an array + * optionally specifying the filter, flags and options. If the value is an + * array, valid keys are filter which specifies the + * filter type, + * flags which specifies any flags that apply to the + * filter, and options which specifies any options that + * apply to the filter. See the example below for a better understanding. + *

+ *

+ * This parameter can be also an integer holding a filter constant. Then all values in the + * input array are filtered by this filter. + *

+ * @param bool $add_empty [optional]

+ * Add missing keys as NULL to the return value. + *

+ * + * @psalm-pure + * + * @return array|false|null + *

+ * An array containing the values of the requested variables on success, or FALSE on failure. + * An array value will be FALSE if the filter fails, or NULL if the variable is not + * set. Or if the flag FILTER_NULL_ON_FAILURE is used, it returns FALSE if the variable + * is not set and NULL if the filter fails. + *

+ */ + public static function filter_input_array( + int $type, + $definition = null, + bool $add_empty = true + ) { + /** + * @psalm-suppress ImpureFunctionCall - we use func_num_args only for args count matching here + */ + if ($definition === null || \func_num_args() < 2) { + $a = \filter_input_array($type); + } else { + $a = \filter_input_array($type, $definition, $add_empty); + } + + /* @phpstan-ignore-next-line | magic frm self::filter :/ */ + return self::filter($a); + } + + /** + * "filter_var()"-wrapper with normalizes to UTF-8 NFC, converting from WINDOWS-1252 when needed. + * + * Filters a variable with a specified filter. + * + * EXAMPLE: UTF8::filter_var('-ABC-中文空白-', FILTER_VALIDATE_URL); // false + * + * @see http://php.net/manual/en/function.filter-var.php + * + * @param float|int|string|null $variable

+ * Value to filter. + *

+ * @param int $filter [optional]

+ * The ID of the filter to apply. The + * manual page lists the available filters. + *

+ * @param int|int[] $options [optional]

+ * Associative array of options or bitwise disjunction of flags. If filter + * accepts options, flags can be provided in "flags" field of array. For + * the "callback" filter, callable type should be passed. The + * callback must accept one argument, the value to be filtered, and return + * the value after filtering/sanitizing it. + *

+ *

+ * + * // for filters that accept options, use this format + * $options = array( + * 'options' => array( + * 'default' => 3, // value to return if the filter fails + * // other options here + * 'min_range' => 0 + * ), + * 'flags' => FILTER_FLAG_ALLOW_OCTAL, + * ); + * $var = filter_var('0755', FILTER_VALIDATE_INT, $options); + * // for filter that only accept flags, you can pass them directly + * $var = filter_var('oops', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + * // for filter that only accept flags, you can also pass as an array + * $var = filter_var('oops', FILTER_VALIDATE_BOOLEAN, + * array('flags' => FILTER_NULL_ON_FAILURE)); + * // callback validate filter + * function foo($value) + * { + * // Expected format: Surname, GivenNames + * if (strpos($value, ", ") === false) return false; + * list($surname, $givennames) = explode(", ", $value, 2); + * $empty = (empty($surname) || empty($givennames)); + * $notstrings = (!is_string($surname) || !is_string($givennames)); + * if ($empty || $notstrings) { + * return false; + * } else { + * return $value; + * } + * } + * $var = filter_var('Doe, Jane Sue', FILTER_CALLBACK, array('options' => 'foo')); + * + *

+ * + * @psalm-pure + * + * @return mixed + *

The filtered data, or FALSE if the filter fails.

+ */ + public static function filter_var( + $variable, + int $filter = \FILTER_DEFAULT, + $options = 0 + ) { + /** + * @psalm-suppress ImpureFunctionCall - we use func_num_args only for args count matching here + */ + if (\func_num_args() < 3) { + $variable = \filter_var($variable, $filter); + } else { + $variable = \filter_var($variable, $filter, $options); + } + + return self::filter($variable); + } + + /** + * "filter_var_array()"-wrapper with normalizes to UTF-8 NFC, converting from WINDOWS-1252 when needed. + * + * Gets multiple variables and optionally filters them. + * + * EXAMPLE: + * $filters = [ + * 'name' => ['filter' => FILTER_CALLBACK, 'options' => [UTF8::class, 'ucwords']], + * 'age' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1, 'max_range' => 120]], + * 'email' => FILTER_VALIDATE_EMAIL, + * ]; + * + * $data = [ + * 'name' => 'κόσμε', + * 'age' => '18', + * 'email' => 'foo@bar.de' + * ]; + * + * UTF8::filter_var_array($data, $filters, true); // ['name' => 'Κόσμε', 'age' => 18, 'email' => 'foo@bar.de'] + * + * + * @see http://php.net/manual/en/function.filter-var-array.php + * + * @param array $data

+ * An array with string keys containing the data to filter. + *

+ * @param array|int $definition [optional]

+ * An array defining the arguments. A valid key is a string + * containing a variable name and a valid value is either a + * filter type, or an + * array optionally specifying the filter, flags and options. + * If the value is an array, valid keys are filter + * which specifies the filter type, + * flags which specifies any flags that apply to the + * filter, and options which specifies any options that + * apply to the filter. See the example below for a better understanding. + *

+ *

+ * This parameter can be also an integer holding a filter constant. Then all values + * in the input array are filtered by this filter. + *

+ * @param bool $add_empty [optional]

+ * Add missing keys as NULL to the return value. + *

+ * + * @psalm-pure + * + * @return array|false|null + *

+ * An array containing the values of the requested variables on success, or FALSE on failure. + * An array value will be FALSE if the filter fails, or NULL if the variable is not + * set. + *

+ */ + public static function filter_var_array( + array $data, + $definition = 0, + bool $add_empty = true + ) { + /** + * @psalm-suppress ImpureFunctionCall - we use func_num_args only for args count matching here + */ + if (\func_num_args() < 2) { + $a = \filter_var_array($data); + } else { + $a = \filter_var_array($data, $definition, $add_empty); + } + + /* @phpstan-ignore-next-line | magic frm self::filter :/ */ + return self::filter($a); + } + + /** + * Checks whether finfo is available on the server. + * + * @psalm-pure + * + * @return bool + *

true if available, false otherwise

+ * + * @internal

Please do not use it anymore, we will make is private in next major version.

+ */ + public static function finfo_loaded(): bool + { + return \class_exists('finfo'); + } + + /** + * Returns the first $n characters of the string. + * + * @param string $str

The input string.

+ * @param int<1, max> $n

Number of characters to retrieve from the start.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + * + * @template T as string + * @phpstan-param T $str + * @phpstan-return (T is non-empty-string ? non-empty-string : string) + */ + public static function first_char( + string $str, + int $n = 1, + string $encoding = 'UTF-8' + ): string { + if ( + $str === '' + || + /* @phpstan-ignore-next-line | we do not trust the phpdoc check */ + $n <= 0 + ) { + return ''; + } + + if ($encoding === 'UTF-8') { + return (string) \mb_substr($str, 0, $n); + } + + return (string) self::substr($str, 0, $n, $encoding); + } + + /** + * Check if the number of Unicode characters isn't greater than the specified integer. + * + * EXAMPLE: UTF8::fits_inside('κόσμε', 6); // false + * + * @param string $str the original string to be checked + * @param int $box_size the size in number of chars to be checked against string + * + * @psalm-pure + * + * @return bool + *

TRUE if string is less than or equal to $box_size, FALSE otherwise.

+ */ + public static function fits_inside(string $str, int $box_size): bool + { + return (int) self::strlen($str) <= $box_size; + } + + /** + * Try to fix simple broken UTF-8 strings. + * + * INFO: Take a look at "UTF8::fix_utf8()" if you need a more advanced fix for broken UTF-8 strings. + * + * EXAMPLE: UTF8::fix_simple_utf8('Düsseldorf'); // 'Düsseldorf' + * + * If you received an UTF-8 string that was converted from Windows-1252 as it was ISO-8859-1 + * (ignoring Windows-1252 chars from 80 to 9F) use this function to fix it. + * See: http://en.wikipedia.org/wiki/Windows-1252 + * + * @param string $str

The input string

+ * + * @psalm-pure + * + * @return string + */ + public static function fix_simple_utf8(string $str): string + { + if ($str === '') { + return ''; + } + + /** + * @psalm-suppress ImpureStaticVariable + * + * @var array|null + */ + static $BROKEN_UTF8_TO_UTF8_KEYS_CACHE = null; + + /** + * @psalm-suppress ImpureStaticVariable + * + * @var array|null + */ + static $BROKEN_UTF8_TO_UTF8_VALUES_CACHE = null; + + if ($BROKEN_UTF8_TO_UTF8_KEYS_CACHE === null) { + if (self::$BROKEN_UTF8_FIX === null) { + self::$BROKEN_UTF8_FIX = self::getData('utf8_fix'); + } + + $BROKEN_UTF8_TO_UTF8_KEYS_CACHE = \array_keys(self::$BROKEN_UTF8_FIX ?: []); + $BROKEN_UTF8_TO_UTF8_VALUES_CACHE = self::$BROKEN_UTF8_FIX; + } + + \assert(\is_array($BROKEN_UTF8_TO_UTF8_VALUES_CACHE)); + + return \str_replace($BROKEN_UTF8_TO_UTF8_KEYS_CACHE, $BROKEN_UTF8_TO_UTF8_VALUES_CACHE, $str); + } + + /** + * Fix a double (or multiple) encoded UTF8 string. + * + * EXAMPLE: UTF8::fix_utf8('Fédération'); // 'Fédération' + * + * @param string|string[] $str you can use a string or an array of strings + * + * @psalm-pure + * + * @return string|string[] + *

Will return the fixed input-"array" or + * the fixed input-"string".

+ * + * @template TFixUtf8 as string|string[] + * @phpstan-param TFixUtf8 $str + * @phpstan-return TFixUtf8 + */ + public static function fix_utf8($str) + { + if (\is_array($str)) { + foreach ($str as &$v) { + $v = self::fix_utf8($v); + } + unset($v); + + /** + * @psalm-suppress InvalidReturnStatement + */ + return $str; + } + + $str = (string) $str; /* @phpstan-ignore-line | TFixUtf8 is string here */ + $last = ''; + while ($last !== $str) { + $last = $str; + /** + * @psalm-suppress PossiblyInvalidArgument + */ + $str = self::to_utf8( + self::utf8_decode($str, true) + ); + } + + /** + * @psalm-suppress InvalidReturnStatement + */ + return $str; + } + + /** + * Get character of a specific character. + * + * EXAMPLE: UTF8::getCharDirection('ا'); // 'RTL' + * + * @param string $char + * + * @psalm-pure + * + * @return string + *

'RTL' or 'LTR'.

+ */ + public static function getCharDirection(string $char): string + { + if (self::$SUPPORT['intlChar'] === true) { + $tmp_return = \IntlChar::charDirection($char); + + // from "IntlChar"-Class + $char_direction = [ + 'RTL' => [1, 13, 14, 15, 21], + 'LTR' => [0, 11, 12, 20], + ]; + + if (\in_array($tmp_return, $char_direction['LTR'], true)) { + return 'LTR'; + } + + if (\in_array($tmp_return, $char_direction['RTL'], true)) { + return 'RTL'; + } + } + + $c = static::chr_to_decimal($char); + + if (!($c >= 0x5be && $c <= 0x10b7f)) { + return 'LTR'; + } + + if ($c <= 0x85e) { + if ($c === 0x5be || + $c === 0x5c0 || + $c === 0x5c3 || + $c === 0x5c6 || + ($c >= 0x5d0 && $c <= 0x5ea) || + ($c >= 0x5f0 && $c <= 0x5f4) || + $c === 0x608 || + $c === 0x60b || + $c === 0x60d || + $c === 0x61b || + ($c >= 0x61e && $c <= 0x64a) || + ($c >= 0x66d && $c <= 0x66f) || + ($c >= 0x671 && $c <= 0x6d5) || + ($c >= 0x6e5 && $c <= 0x6e6) || + ($c >= 0x6ee && $c <= 0x6ef) || + ($c >= 0x6fa && $c <= 0x70d) || + $c === 0x710 || + ($c >= 0x712 && $c <= 0x72f) || + ($c >= 0x74d && $c <= 0x7a5) || + $c === 0x7b1 || + ($c >= 0x7c0 && $c <= 0x7ea) || + ($c >= 0x7f4 && $c <= 0x7f5) || + $c === 0x7fa || + ($c >= 0x800 && $c <= 0x815) || + $c === 0x81a || + $c === 0x824 || + $c === 0x828 || + ($c >= 0x830 && $c <= 0x83e) || + ($c >= 0x840 && $c <= 0x858) || + $c === 0x85e + ) { + return 'RTL'; + } + } elseif ($c === 0x200f) { + return 'RTL'; + } elseif ($c >= 0xfb1d) { + if ($c === 0xfb1d || + ($c >= 0xfb1f && $c <= 0xfb28) || + ($c >= 0xfb2a && $c <= 0xfb36) || + ($c >= 0xfb38 && $c <= 0xfb3c) || + $c === 0xfb3e || + ($c >= 0xfb40 && $c <= 0xfb41) || + ($c >= 0xfb43 && $c <= 0xfb44) || + ($c >= 0xfb46 && $c <= 0xfbc1) || + ($c >= 0xfbd3 && $c <= 0xfd3d) || + ($c >= 0xfd50 && $c <= 0xfd8f) || + ($c >= 0xfd92 && $c <= 0xfdc7) || + ($c >= 0xfdf0 && $c <= 0xfdfc) || + ($c >= 0xfe70 && $c <= 0xfe74) || + ($c >= 0xfe76 && $c <= 0xfefc) || + ($c >= 0x10800 && $c <= 0x10805) || + $c === 0x10808 || + ($c >= 0x1080a && $c <= 0x10835) || + ($c >= 0x10837 && $c <= 0x10838) || + $c === 0x1083c || + ($c >= 0x1083f && $c <= 0x10855) || + ($c >= 0x10857 && $c <= 0x1085f) || + ($c >= 0x10900 && $c <= 0x1091b) || + ($c >= 0x10920 && $c <= 0x10939) || + $c === 0x1093f || + $c === 0x10a00 || + ($c >= 0x10a10 && $c <= 0x10a13) || + ($c >= 0x10a15 && $c <= 0x10a17) || + ($c >= 0x10a19 && $c <= 0x10a33) || + ($c >= 0x10a40 && $c <= 0x10a47) || + ($c >= 0x10a50 && $c <= 0x10a58) || + ($c >= 0x10a60 && $c <= 0x10a7f) || + ($c >= 0x10b00 && $c <= 0x10b35) || + ($c >= 0x10b40 && $c <= 0x10b55) || + ($c >= 0x10b58 && $c <= 0x10b72) || + ($c >= 0x10b78) + ) { + return 'RTL'; + } + } + + return 'LTR'; + } + + /** + * Check for php-support. + * + * @param string|null $key + * + * @psalm-pure + * + * @return mixed + * Return the full support-"array", if $key === null
+ * return bool-value, if $key is used and available
+ * otherwise return null + */ + public static function getSupportInfo(string $key = null) + { + if ($key === null) { + return self::$SUPPORT; + } + + if (self::$INTL_TRANSLITERATOR_LIST === null) { + self::$INTL_TRANSLITERATOR_LIST = self::getData('transliterator_list'); + } + // compatibility fix for old versions + self::$SUPPORT['intl__transliterator_list_ids'] = self::$INTL_TRANSLITERATOR_LIST; + + return self::$SUPPORT[$key] ?? null; + } + + /** + * Warning: this method only works for some file-types (png, jpg) + * if you need more supported types, please use e.g. "finfo" + * + * @param string $str + * @param array{ext: null|string, mime: null|string, type: null|string} $fallback + * + * @return array{ext: null|string, mime: null|string, type: null|string} + * + * @psalm-pure + */ + public static function get_file_type( + string $str, + array $fallback = [ + 'ext' => null, + 'mime' => 'application/octet-stream', + 'type' => null, + ] + ): array { + if ($str === '') { + return $fallback; + } + + /** @var false|string $str_info - needed for PhpStan (stubs error) */ + $str_info = \substr($str, 0, 2); + if ($str_info === false || \strlen($str_info) !== 2) { + return $fallback; + } + + // DEBUG + //var_dump($str_info); + + $str_info = \unpack('C2chars', $str_info); + + if ($str_info === false) { + return $fallback; + } + $type_code = (int) ($str_info['chars1'] . $str_info['chars2']); + + // DEBUG + //var_dump($type_code); + + // + // info: https://en.wikipedia.org/wiki/Magic_number_%28programming%29#Format_indicator + // + switch ($type_code) { + // WARNING: do not add too simple comparisons, because of false-positive results: + // + // 3780 => 'pdf', 7790 => 'exe', 7784 => 'midi', 8075 => 'zip', + // 8297 => 'rar', 7173 => 'gif', 7373 => 'tiff' 6677 => 'bmp', ... + // + case 255216: + $ext = 'jpg'; + $mime = 'image/jpeg'; + $type = 'binary'; + + break; + case 13780: + $ext = 'png'; + $mime = 'image/png'; + $type = 'binary'; + + break; + default: + return $fallback; + } + + return [ + 'ext' => $ext, + 'mime' => $mime, + 'type' => $type, + ]; + } + + /** + * @param int<1, max> $length

Length of the random string.

+ * @param string $possible_chars [optional]

Characters string for the random selection.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @return string + * + * @template T as string + * @phpstan-param T $possible_chars + * @phpstan-return (T is non-empty-string ? non-empty-string : '') + */ + public static function get_random_string( + int $length, + string $possible_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', + string $encoding = 'UTF-8' + ): string { + // init + $i = 0; + $str = ''; + + // + // add random chars + // + + if ($encoding === 'UTF-8') { + $max_length = (int) \mb_strlen($possible_chars); + if ($max_length === 0) { + return ''; + } + + while ($i < $length) { + try { + $rand_int = \random_int(0, $max_length - 1); + } catch (\Exception $e) { + $rand_int = \mt_rand(0, $max_length - 1); + } + $char = \mb_substr($possible_chars, $rand_int, 1); + /* @phpstan-ignore-next-line | "false" was at least the return type in the past, or? */ + if ($char !== false) { + $str .= $char; + ++$i; + } + } + } else { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + $max_length = (int) self::strlen($possible_chars, $encoding); + if ($max_length === 0) { + return ''; + } + + while ($i < $length) { + try { + $rand_int = \random_int(0, $max_length - 1); + } catch (\Exception $e) { + $rand_int = \mt_rand(0, $max_length - 1); + } + $char = self::substr($possible_chars, $rand_int, 1, $encoding); + if ($char !== false) { + $str .= $char; + ++$i; + } + } + } + + return $str; + } + + /** + * @param int|string $extra_entropy [optional]

Extra entropy via a string or int value.

+ * @param bool $use_md5 [optional]

Return the unique identifier as md5-hash? Default: true

+ * + * @return non-empty-string + */ + public static function get_unique_string($extra_entropy = '', bool $use_md5 = true): string + { + try { + $rand_int = \random_int(0, \mt_getrandmax()); + } catch (\Exception $e) { + $rand_int = \mt_rand(0, \mt_getrandmax()); + } + + $unique_helper = $rand_int . + \session_id() . + ($_SERVER['REMOTE_ADDR'] ?? '') . + ($_SERVER['SERVER_ADDR'] ?? '') . + $extra_entropy; + + $unique_string = \uniqid($unique_helper, true); + + if ($use_md5) { + $unique_string = \md5($unique_string . $unique_helper); + } + + return $unique_string; + } + + /** + * Returns true if the string contains a lower case char, false otherwise. + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return bool + *

Whether or not the string contains a lower case character.

+ */ + public static function has_lowercase(string $str): bool + { + if (self::$SUPPORT['mbstring'] === true) { + return \mb_ereg_match('.*[[:lower:]]', $str); + } + + return self::str_matches_pattern($str, '.*[[:lower:]]'); + } + + /** + * Returns true if the string contains whitespace, false otherwise. + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return bool + *

Whether or not the string contains whitespace.

+ */ + public static function has_whitespace(string $str): bool + { + if (self::$SUPPORT['mbstring'] === true) { + return \mb_ereg_match('.*[[:space:]]', $str); + } + + return self::str_matches_pattern($str, '.*[[:space:]]'); + } + + /** + * Returns true if the string contains an upper case char, false otherwise. + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return bool + *

Whether or not the string contains an upper case character.

+ */ + public static function has_uppercase(string $str): bool + { + if (self::$SUPPORT['mbstring'] === true) { + return \mb_ereg_match('.*[[:upper:]]', $str); + } + + return self::str_matches_pattern($str, '.*[[:upper:]]'); + } + + /** + * Converts a hexadecimal value into a UTF-8 character. + * + * INFO: opposite to UTF8::chr_to_hex() + * + * EXAMPLE: UTF8::hex_to_chr('U+00a7'); // '§' + * + * @param string $hexdec

The hexadecimal value.

+ * + * @psalm-pure + * + * @return string + *

One single UTF-8 character.

+ */ + public static function hex_to_chr(string $hexdec) + { + /** @noinspection PhpUsageOfSilenceOperatorInspection - Invalid characters passed for attempted conversion, these have been ignored */ + return self::decimal_to_chr((int) @\hexdec($hexdec)); + } + + /** + * Converts hexadecimal U+xxxx code point representation to integer. + * + * INFO: opposite to UTF8::int_to_hex() + * + * EXAMPLE: UTF8::hex_to_int('U+00f1'); // 241 + * + * @param string $hexdec

The hexadecimal code point representation.

+ * + * @psalm-pure + * + * @return false|int + *

The code point, or false on failure.

+ */ + public static function hex_to_int($hexdec) + { + // init + $hexdec = (string) $hexdec; + + if ($hexdec === '') { + return false; + } + + if (\preg_match('/^(?:\\\u|U\+|)([a-zA-Z0-9]{4,6})$/', $hexdec, $match)) { + return \intval($match[1], 16); + } + + return false; + } + + /** + * Converts a UTF-8 string to a series of HTML numbered entities. + * + * INFO: opposite to UTF8::html_decode() + * + * EXAMPLE: UTF8::html_encode('中文空白'); // '中文空白' + * + * @param string $str

The Unicode string to be encoded as numbered entities.

+ * @param bool $keep_ascii_chars [optional]

Keep ASCII chars.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + *

HTML numbered entities.

+ * + * @template T as string + * @phpstan-param T $str + * @phpstan-return (T is non-empty-string ? non-empty-string : string) + */ + public static function html_encode( + string $str, + bool $keep_ascii_chars = false, + string $encoding = 'UTF-8' + ): string { + if ($str === '') { + return ''; + } + + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + // INFO: http://stackoverflow.com/questions/35854535/better-explanation-of-convmap-in-mb-encode-numericentity + if (self::$SUPPORT['mbstring'] === true) { + if ($keep_ascii_chars) { + $start_code = 0x80; + } else { + $start_code = 0x00; + } + + if ($encoding === 'UTF-8') { + /** @var false|string|null $return - needed for PhpStan (stubs error) */ + $return = \mb_encode_numericentity( + $str, + [$start_code, 0xfffff, 0, 0xfffff] + ); + if ($return !== null && $return !== false) { + return $return; + } + } + + /** @var false|string|null $return - needed for PhpStan (stubs error) */ + $return = \mb_encode_numericentity( + $str, + [$start_code, 0xfffff, 0, 0xfffff], + $encoding + ); + if ($return !== null && $return !== false) { + return $return; + } + } + + // + // fallback via vanilla php + // + + return \implode( + '', + \array_map( + static function (string $chr) use ($keep_ascii_chars, $encoding): string { + return self::single_chr_html_encode($chr, $keep_ascii_chars, $encoding); + }, + self::str_split($str) + ) + ); + } + + /** + * UTF-8 version of html_entity_decode() + * + * The reason we are not using html_entity_decode() by itself is because + * while it is not technically correct to leave out the semicolon + * at the end of an entity most browsers will still interpret the entity + * correctly. html_entity_decode() does not convert entities without + * semicolons, so we are left with our own little solution here. Bummer. + * + * Convert all HTML entities to their applicable characters. + * + * INFO: opposite to UTF8::html_encode() + * + * EXAMPLE: UTF8::html_entity_decode('中文空白'); // '中文空白' + * + * @see http://php.net/manual/en/function.html-entity-decode.php + * + * @param string $str

+ * The input string. + *

+ * @param int|null $flags [optional]

+ * A bitmask of one or more of the following flags, which specify how to handle quotes + * and which document type to use. The default is ENT_COMPAT | ENT_HTML401. + * + * Available flags constants + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Constant NameDescription
ENT_COMPATWill convert double-quotes and leave single-quotes alone.
ENT_QUOTESWill convert both double and single quotes.
ENT_NOQUOTESWill leave both double and single quotes unconverted.
ENT_HTML401 + * Handle code as HTML 4.01. + *
ENT_XML1 + * Handle code as XML 1. + *
ENT_XHTML + * Handle code as XHTML. + *
ENT_HTML5 + * Handle code as HTML 5. + *
+ *

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + *

The decoded string.

+ * + * @template T as string + * @phpstan-param T $str + * @phpstan-return (T is non-empty-string ? non-empty-string : string) + */ + public static function html_entity_decode( + string $str, + int $flags = null, + string $encoding = 'UTF-8' + ): string { + if ( + !isset($str[3]) // examples: &; || &x; + || + \strpos($str, '&') === false // no "&" + ) { + return $str; + } + + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + if ($flags === null) { + $flags = \ENT_QUOTES | \ENT_HTML5; + } + + if ( + $encoding !== 'UTF-8' + && + $encoding !== 'ISO-8859-1' + && + $encoding !== 'WINDOWS-1252' + && + self::$SUPPORT['mbstring'] === false + ) { + /** + * @psalm-suppress ImpureFunctionCall - this is only a warning + */ + \trigger_error('UTF8::html_entity_decode() without mbstring cannot handle "' . $encoding . '" encoding', \E_USER_WARNING); + } + + do { + $str_compare = $str; + + if (\strpos($str, '&') !== false) { + if (\strpos($str, '&#') !== false) { + // decode also numeric & UTF16 two byte entities + $str = (string) \preg_replace( + '/(&#(?:x0*[0-9a-fA-F]{2,6}(?![0-9a-fA-F;])|(?:0*\d{2,6}(?![0-9;]))))/S', + '$1;', + $str + ); + } + + $str = \html_entity_decode( + $str, + $flags, + $encoding + ); + } + } while ($str_compare !== $str); + + return $str; + } + + /** + * Create a escape html version of the string via "UTF8::htmlspecialchars()". + * + * @param string $str + * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + */ + public static function html_escape(string $str, string $encoding = 'UTF-8'): string + { + return self::htmlspecialchars( + $str, + \ENT_QUOTES | \ENT_SUBSTITUTE, + $encoding + ); + } + + /** + * Remove empty html-tag. + * + * e.g.:
+ * + * @param string $str + * + * @psalm-pure + * + * @return string + */ + public static function html_stripe_empty_tags(string $str): string + { + return (string) \preg_replace( + '/<[^\\/>]*?>\\s*?<\\/[^>]*?>/u', + '', + $str + ); + } + + /** + * Convert all applicable characters to HTML entities: UTF-8 version of htmlentities(). + * + * EXAMPLE: UTF8::htmlentities('<白-öäü>'); // '<白-öäü>' + * + * @see http://php.net/manual/en/function.htmlentities.php + * + * @param string $str

+ * The input string. + *

+ * @param int $flags [optional]

+ * A bitmask of one or more of the following flags, which specify how to handle + * quotes, invalid code unit sequences and the used document type. The default is + * ENT_COMPAT | ENT_HTML401. + * + * Available flags constants + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Constant NameDescription
ENT_COMPATWill convert double-quotes and leave single-quotes alone.
ENT_QUOTESWill convert both double and single quotes.
ENT_NOQUOTESWill leave both double and single quotes unconverted.
ENT_IGNORE + * Silently discard invalid code unit sequences instead of returning + * an empty string. Using this flag is discouraged as it + * may have security implications. + *
ENT_SUBSTITUTE + * Replace invalid code unit sequences with a Unicode Replacement Character + * U+FFFD (UTF-8) or &#38;#FFFD; (otherwise) instead of returning an empty + * string. + *
ENT_DISALLOWED + * Replace invalid code points for the given document type with a + * Unicode Replacement Character U+FFFD (UTF-8) or &#38;#FFFD; + * (otherwise) instead of leaving them as is. This may be useful, for + * instance, to ensure the well-formedness of XML documents with + * embedded external content. + *
ENT_HTML401 + * Handle code as HTML 4.01. + *
ENT_XML1 + * Handle code as XML 1. + *
ENT_XHTML + * Handle code as XHTML. + *
ENT_HTML5 + * Handle code as HTML 5. + *
+ *

+ * @param string $encoding [optional]

+ * Like htmlspecialchars, + * htmlentities takes an optional third argument + * encoding which defines encoding used in + * conversion. + * Although this argument is technically optional, you are highly + * encouraged to specify the correct value for your code. + *

+ * @param bool $double_encode [optional]

+ * When double_encode is turned off PHP will not + * encode existing html entities. The default is to convert everything. + *

+ * + * @psalm-pure + * + * @return string + *

+ * The encoded string. + *

+ * If the input string contains an invalid code unit + * sequence within the given encoding an empty string + * will be returned, unless either the ENT_IGNORE or + * ENT_SUBSTITUTE flags are set. + *

+ */ + public static function htmlentities( + string $str, + int $flags = \ENT_COMPAT, + string $encoding = 'UTF-8', + bool $double_encode = true + ): string { + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + $str = \htmlentities( + $str, + $flags, + $encoding, + $double_encode + ); + + /** + * PHP doesn't replace a backslash to its html entity since this is something + * that's mostly used to escape characters when inserting in a database. Since + * we're using a decent database layer, we don't need this shit and we're replacing + * the double backslashes by its' html entity equivalent. + * + * https://github.com/forkcms/library/blob/master/spoon/filter/filter.php#L303 + */ + $str = \str_replace('\\', '\', $str); + + return self::html_encode($str, true, $encoding); + } + + /** + * Convert only special characters to HTML entities: UTF-8 version of htmlspecialchars() + * + * INFO: Take a look at "UTF8::htmlentities()" + * + * EXAMPLE: UTF8::htmlspecialchars('<白-öäü>'); // '<白-öäü>' + * + * @see http://php.net/manual/en/function.htmlspecialchars.php + * + * @param string $str

+ * The string being converted. + *

+ * @param int $flags [optional]

+ * A bitmask of one or more of the following flags, which specify how to handle + * quotes, invalid code unit sequences and the used document type. The default is + * ENT_COMPAT | ENT_HTML401. + * + * Available flags constants + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Constant NameDescription
ENT_COMPATWill convert double-quotes and leave single-quotes alone.
ENT_QUOTESWill convert both double and single quotes.
ENT_NOQUOTESWill leave both double and single quotes unconverted.
ENT_IGNORE + * Silently discard invalid code unit sequences instead of returning + * an empty string. Using this flag is discouraged as it + * may have security implications. + *
ENT_SUBSTITUTE + * Replace invalid code unit sequences with a Unicode Replacement Character + * U+FFFD (UTF-8) or &#38;#FFFD; (otherwise) instead of returning an empty + * string. + *
ENT_DISALLOWED + * Replace invalid code points for the given document type with a + * Unicode Replacement Character U+FFFD (UTF-8) or &#38;#FFFD; + * (otherwise) instead of leaving them as is. This may be useful, for + * instance, to ensure the well-formedness of XML documents with + * embedded external content. + *
ENT_HTML401 + * Handle code as HTML 4.01. + *
ENT_XML1 + * Handle code as XML 1. + *
ENT_XHTML + * Handle code as XHTML. + *
ENT_HTML5 + * Handle code as HTML 5. + *
+ *

+ * @param string $encoding [optional]

+ * Defines encoding used in conversion. + *

+ *

+ * For the purposes of this function, the encodings + * ISO-8859-1, ISO-8859-15, + * UTF-8, cp866, + * cp1251, cp1252, and + * KOI8-R are effectively equivalent, provided the + * string itself is valid for the encoding, as + * the characters affected by htmlspecialchars occupy + * the same positions in all of these encodings. + *

+ * @param bool $double_encode [optional]

+ * When double_encode is turned off PHP will not + * encode existing html entities, the default is to convert everything. + *

+ * + * @psalm-pure + * + * @return string + *

The converted string.

+ *

+ * If the input string contains an invalid code unit + * sequence within the given encoding an empty string + * will be returned, unless either the ENT_IGNORE or + * ENT_SUBSTITUTE flags are set.

+ * + * @template T as string + * @phpstan-param T $str + * @phpstan-return (T is non-empty-string ? non-empty-string : string) + */ + public static function htmlspecialchars( + string $str, + int $flags = \ENT_COMPAT, + string $encoding = 'UTF-8', + bool $double_encode = true + ): string { + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + return \htmlspecialchars( + $str, + $flags, + $encoding, + $double_encode + ); + } + + /** + * Checks whether iconv is available on the server. + * + * @psalm-pure + * + * @return bool + *

true if available, false otherwise

+ * + * @internal

Please do not use it anymore, we will make is private in next major version.

+ */ + public static function iconv_loaded(): bool + { + return \extension_loaded('iconv'); + } + + /** + * Converts Integer to hexadecimal U+xxxx code point representation. + * + * INFO: opposite to UTF8::hex_to_int() + * + * EXAMPLE: UTF8::int_to_hex(241); // 'U+00f1' + * + * @param int $int

The integer to be converted to hexadecimal code point.

+ * @param string $prefix [optional] + * + * @psalm-pure + * + * @return string the code point, or empty string on failure + */ + public static function int_to_hex(int $int, string $prefix = 'U+'): string + { + $hex = \dechex($int); + + $hex = (\strlen($hex) < 4 ? \substr('0000' . $hex, -4) : $hex); + + return $prefix . $hex . ''; + } + + /** + * Checks whether intl-char is available on the server. + * + * @psalm-pure + * + * @return bool + *

true if available, false otherwise

+ * + * @internal

Please do not use it anymore, we will make is private in next major version.

+ */ + public static function intlChar_loaded(): bool + { + return \class_exists('IntlChar'); + } + + /** + * Checks whether intl is available on the server. + * + * @psalm-pure + * + * @return bool + *

true if available, false otherwise

+ * + * @internal

Please do not use it anymore, we will make is private in next major version.

+ */ + public static function intl_loaded(): bool + { + return \extension_loaded('intl'); + } + + /** + * Returns true if the string contains only alphabetic chars, false otherwise. + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return bool + *

Whether or not $str contains only alphabetic chars.

+ */ + public static function is_alpha(string $str): bool + { + if (self::$SUPPORT['mbstring'] === true) { + return \mb_ereg_match('^[[:alpha:]]*$', $str); + } + + return self::str_matches_pattern($str, '^[[:alpha:]]*$'); + } + + /** + * Returns true if the string contains only alphabetic and numeric chars, false otherwise. + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return bool + *

Whether or not $str contains only alphanumeric chars.

+ */ + public static function is_alphanumeric(string $str): bool + { + if (self::$SUPPORT['mbstring'] === true) { + return \mb_ereg_match('^[[:alnum:]]*$', $str); + } + + return self::str_matches_pattern($str, '^[[:alnum:]]*$'); + } + + /** + * Returns true if the string contains only punctuation chars, false otherwise. + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return bool + *

Whether or not $str contains only punctuation chars.

+ */ + public static function is_punctuation(string $str): bool + { + return self::str_matches_pattern($str, '^[[:punct:]]*$'); + } + + /** + * Returns true if the string contains only printable (non-invisible) chars, false otherwise. + * + * @param string $str

The input string.

+ * @param bool $ignore_control_characters [optional]

Ignore control characters like [LRM] or [LSEP].

+ * + * @psalm-pure + * + * @return bool + *

Whether or not $str contains only printable (non-invisible) chars.

+ */ + public static function is_printable(string $str, bool $ignore_control_characters = false): bool + { + return self::remove_invisible_characters($str, false, '', $ignore_control_characters) === $str; + } + + /** + * Checks if a string is 7 bit ASCII. + * + * EXAMPLE: UTF8::is_ascii('白'); // false + * + * @param string $str

The string to check.

+ * + * @psalm-pure + * + * @return bool + *

+ * true if it is ASCII
+ * false otherwise + *

+ */ + public static function is_ascii(string $str): bool + { + return ASCII::is_ascii($str); + } + + /** + * Returns true if the string is base64 encoded, false otherwise. + * + * EXAMPLE: UTF8::is_base64('4KSu4KWL4KSo4KS/4KSa'); // true + * + * @param string|null $str

The input string.

+ * @param bool $empty_string_is_valid [optional]

Is an empty string valid base64 or not?

+ * + * @psalm-pure + * + * @return bool + *

Whether or not $str is base64 encoded.

+ */ + public static function is_base64($str, bool $empty_string_is_valid = false): bool + { + if ( + !$empty_string_is_valid + && + $str === '' + ) { + return false; + } + + if (!\is_string($str)) { + return false; + } + + $base64String = \base64_decode($str, true); + + return $base64String !== false && \base64_encode($base64String) === $str; + } + + /** + * Check if the input is binary... (is look like a hack). + * + * EXAMPLE: UTF8::is_binary(01); // true + * + * @param int|string $input + * @param bool $strict + * + * @psalm-pure + * + * @return bool + */ + public static function is_binary($input, bool $strict = false): bool + { + $input = (string) $input; + if ($input === '') { + return false; + } + + if (\preg_match('~^[01]+$~', $input)) { + return true; + } + + $ext = self::get_file_type($input); + if ($ext['type'] === 'binary') { + return true; + } + + if (!$strict) { + $test_length = \strlen($input); + $test_null_counting = \substr_count($input, "\x0", 0, $test_length); + if (($test_null_counting / $test_length) > 0.25) { + return true; + } + } + + if ($strict) { + if (self::$SUPPORT['finfo'] === false) { + throw new \RuntimeException('ext-fileinfo: is not installed'); + } + + /** + * @psalm-suppress ImpureMethodCall - it will return the same result for the same file ... + */ + $finfo_encoding = (new \finfo(\FILEINFO_MIME_ENCODING))->buffer($input); + if ($finfo_encoding && $finfo_encoding === 'binary') { + return true; + } + } + + return false; + } + + /** + * Check if the file is binary. + * + * EXAMPLE: UTF8::is_binary('./utf32.txt'); // true + * + * @param string $file + * + * @return bool + */ + public static function is_binary_file($file): bool + { + // init + $block = ''; + + $fp = \fopen($file, 'rb'); + if (\is_resource($fp)) { + $block = \fread($fp, 512); + \fclose($fp); + } + + if ($block === '' || $block === false) { + return false; + } + + return self::is_binary($block, true); + } + + /** + * Returns true if the string contains only whitespace chars, false otherwise. + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return bool + *

Whether or not $str contains only whitespace characters.

+ */ + public static function is_blank(string $str): bool + { + if (self::$SUPPORT['mbstring'] === true) { + return \mb_ereg_match('^[[:space:]]*$', $str); + } + + return self::str_matches_pattern($str, '^[[:space:]]*$'); + } + + /** + * Checks if the given string is equal to any "Byte Order Mark". + * + * WARNING: Use "UTF8::string_has_bom()" if you will check BOM in a string. + * + * EXAMPLE: UTF8::is_bom("\xef\xbb\xbf"); // true + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return bool + *

true if the $utf8_chr is Byte Order Mark, false otherwise.

+ */ + public static function is_bom($str): bool + { + /** @noinspection PhpUnusedLocalVariableInspection */ + foreach (self::$BOM as $bom_string => &$bom_byte_length) { + if ($str === $bom_string) { + return true; + } + } + + return false; + } + + /** + * Determine whether the string is considered to be empty. + * + * A variable is considered empty if it does not exist or if its value equals FALSE. + * empty() does not generate a warning if the variable does not exist. + * + * @param array|float|int|string $str + * + * @psalm-pure + * + * @return bool + *

Whether or not $str is empty().

+ */ + public static function is_empty($str): bool + { + return empty($str); + } + + /** + * Returns true if the string contains only hexadecimal chars, false otherwise. + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return bool + *

Whether or not $str contains only hexadecimal chars.

+ */ + public static function is_hexadecimal(string $str): bool + { + if (self::$SUPPORT['mbstring'] === true) { + return \mb_ereg_match('^[[:xdigit:]]*$', $str); + } + + return self::str_matches_pattern($str, '^[[:xdigit:]]*$'); + } + + /** + * Check if the string contains any HTML tags. + * + * EXAMPLE: UTF8::is_html('lall'); // true + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return bool + *

Whether or not $str contains html elements.

+ */ + public static function is_html(string $str): bool + { + if ($str === '') { + return false; + } + + // init + $matches = []; + + $str = self::emoji_encode($str); // hack for emoji support :/ + + \preg_match("/<\\/?\\w+(?:(?:\\s+\\w+(?:\\s*=\\s*(?:\".*?\"|'.*?'|[^'\">\\s]+))?)*\\s*|\\s*)\\/?>/u", $str, $matches); + + return $matches !== []; + } + + /** + * Check if $url is an correct url. + * + * @param string $url + * @param bool $disallow_localhost + * + * @psalm-pure + * + * @return bool + */ + public static function is_url(string $url, bool $disallow_localhost = false): bool + { + if ($url === '') { + return false; + } + + // WARNING: keep this as hack protection + if (!self::str_istarts_with_any($url, ['http://', 'https://'])) { + return false; + } + + // e.g. -> the server itself connect to "https://foo.localhost/phpmyadmin/... + if ($disallow_localhost) { + if (self::str_istarts_with_any( + $url, + [ + 'http://localhost', + 'https://localhost', + 'http://127.0.0.1', + 'https://127.0.0.1', + 'http://::1', + 'https://::1', + ] + )) { + return false; + } + + $regex = '/^(?:http(?:s)?:\/\/).*?(?:\.localhost)/iu'; + if (\preg_match($regex, $url)) { + return false; + } + } + + // INFO: this is needed for e.g. "http://müller.de/" (internationalized domain names) and non ASCII-parameters + $regex = '/^(?:http(?:s)?:\\/\\/)(?:[\p{L}0-9][\p{L}0-9_-]*(?:\\.[\p{L}0-9][\p{L}0-9_-]*))(?:\\d+)?(?:\\/\\.*)?/iu'; + if (\preg_match($regex, $url)) { + return true; + } + + return \filter_var($url, \FILTER_VALIDATE_URL) !== false; + } + + /** + * Try to check if "$str" is a JSON-string. + * + * EXAMPLE: UTF8::is_json('{"array":[1,"¥","ä"]}'); // true + * + * @param string $str

The input string.

+ * @param bool $only_array_or_object_results_are_valid [optional]

Only array and objects are valid json + * results.

+ * + * @return bool + *

Whether or not the $str is in JSON format.

+ */ + public static function is_json(string $str, bool $only_array_or_object_results_are_valid = true): bool + { + if ($str === '') { + return false; + } + + if (self::$SUPPORT['json'] === false) { + throw new \RuntimeException('ext-json: is not installed'); + } + + $jsonOrNull = self::json_decode($str); + if ($jsonOrNull === null && \strtoupper($str) !== 'NULL') { + return false; + } + + if ( + $only_array_or_object_results_are_valid + && + !\is_object($jsonOrNull) + && + !\is_array($jsonOrNull) + ) { + return false; + } + + return \json_last_error() === \JSON_ERROR_NONE; + } + + /** + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return bool + *

Whether or not $str contains only lowercase chars.

+ */ + public static function is_lowercase(string $str): bool + { + if (self::$SUPPORT['mbstring'] === true) { + return \mb_ereg_match('^[[:lower:]]*$', $str); + } + + return self::str_matches_pattern($str, '^[[:lower:]]*$'); + } + + /** + * Returns true if the string is serialized, false otherwise. + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return bool + *

Whether or not $str is serialized.

+ */ + public static function is_serialized(string $str): bool + { + if ($str === '') { + return false; + } + + /** @noinspection PhpUsageOfSilenceOperatorInspection */ + /** @noinspection UnserializeExploitsInspection */ + return $str === 'b:0;' + || + @\unserialize($str, []) !== false; + } + + /** + * Returns true if the string contains only lower case chars, false + * otherwise. + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return bool + *

Whether or not $str contains only lower case characters.

+ */ + public static function is_uppercase(string $str): bool + { + if (self::$SUPPORT['mbstring'] === true) { + return \mb_ereg_match('^[[:upper:]]*$', $str); + } + + return self::str_matches_pattern($str, '^[[:upper:]]*$'); + } + + /** + * Check if the string is UTF-16. + * + * EXAMPLE: + * UTF8::is_utf16(file_get_contents('utf-16-le.txt')); // 1 + * // + * UTF8::is_utf16(file_get_contents('utf-16-be.txt')); // 2 + * // + * UTF8::is_utf16(file_get_contents('utf-8.txt')); // false + * + * + * @param string $str

The input string.

+ * @param bool $check_if_string_is_binary + * + * @psalm-pure + * + * @return false|int + * false if is't not UTF-16,
+ * 1 for UTF-16LE,
+ * 2 for UTF-16BE + */ + public static function is_utf16($str, bool $check_if_string_is_binary = true) + { + // init + $str = (string) $str; + $str_chars = []; + + // fix for the "binary"-check + if ($check_if_string_is_binary !== false && self::string_has_bom($str)) { + $check_if_string_is_binary = false; + } + + if ( + $check_if_string_is_binary + && + !self::is_binary($str, true) + ) { + return false; + } + + if (self::$SUPPORT['mbstring'] === false) { + /** + * @psalm-suppress ImpureFunctionCall - this is only a warning + */ + \trigger_error('UTF8::is_utf16() without mbstring may did not work correctly', \E_USER_WARNING); + } + + $str = self::remove_bom($str); + + $maybe_utf16le = 0; + $test = \mb_convert_encoding($str, 'UTF-8', 'UTF-16LE'); + if ($test) { + $test2 = \mb_convert_encoding($test, 'UTF-16LE', 'UTF-8'); + $test3 = \mb_convert_encoding($test2, 'UTF-8', 'UTF-16LE'); + if ($test3 === $test) { + $str_chars = self::count_chars($str, true, false); + foreach (self::count_chars($test3) as $test3char => &$test3charEmpty) { + if (\in_array($test3char, $str_chars, true)) { + ++$maybe_utf16le; + } + } + unset($test3charEmpty); + } + } + + $maybe_utf16be = 0; + $test = \mb_convert_encoding($str, 'UTF-8', 'UTF-16BE'); + if ($test) { + $test2 = \mb_convert_encoding($test, 'UTF-16BE', 'UTF-8'); + $test3 = \mb_convert_encoding($test2, 'UTF-8', 'UTF-16BE'); + if ($test3 === $test) { + if ($str_chars === []) { + $str_chars = self::count_chars($str, true, false); + } + foreach (self::count_chars($test3) as $test3char => &$test3charEmpty) { + if (\in_array($test3char, $str_chars, true)) { + ++$maybe_utf16be; + } + } + unset($test3charEmpty); + } + } + + if ($maybe_utf16be !== $maybe_utf16le) { + if ($maybe_utf16le > $maybe_utf16be) { + return 1; + } + + return 2; + } + + return false; + } + + /** + * Check if the string is UTF-32. + * + * EXAMPLE: + * UTF8::is_utf32(file_get_contents('utf-32-le.txt')); // 1 + * // + * UTF8::is_utf32(file_get_contents('utf-32-be.txt')); // 2 + * // + * UTF8::is_utf32(file_get_contents('utf-8.txt')); // false + * + * + * @param string $str

The input string.

+ * @param bool $check_if_string_is_binary + * + * @psalm-pure + * + * @return false|int + * false if is't not UTF-32,
+ * 1 for UTF-32LE,
+ * 2 for UTF-32BE + */ + public static function is_utf32($str, bool $check_if_string_is_binary = true) + { + // init + $str = (string) $str; + $str_chars = []; + + // fix for the "binary"-check + if ($check_if_string_is_binary !== false && self::string_has_bom($str)) { + $check_if_string_is_binary = false; + } + + if ( + $check_if_string_is_binary + && + !self::is_binary($str, true) + ) { + return false; + } + + if (self::$SUPPORT['mbstring'] === false) { + /** + * @psalm-suppress ImpureFunctionCall - this is only a warning + */ + \trigger_error('UTF8::is_utf32() without mbstring may did not work correctly', \E_USER_WARNING); + } + + $str = self::remove_bom($str); + + $maybe_utf32le = 0; + $test = \mb_convert_encoding($str, 'UTF-8', 'UTF-32LE'); + if ($test) { + $test2 = \mb_convert_encoding($test, 'UTF-32LE', 'UTF-8'); + $test3 = \mb_convert_encoding($test2, 'UTF-8', 'UTF-32LE'); + if ($test3 === $test) { + $str_chars = self::count_chars($str, true, false); + foreach (self::count_chars($test3) as $test3char => &$test3charEmpty) { + if (\in_array($test3char, $str_chars, true)) { + ++$maybe_utf32le; + } + } + unset($test3charEmpty); + } + } + + $maybe_utf32be = 0; + $test = \mb_convert_encoding($str, 'UTF-8', 'UTF-32BE'); + if ($test) { + $test2 = \mb_convert_encoding($test, 'UTF-32BE', 'UTF-8'); + $test3 = \mb_convert_encoding($test2, 'UTF-8', 'UTF-32BE'); + if ($test3 === $test) { + if ($str_chars === []) { + $str_chars = self::count_chars($str, true, false); + } + foreach (self::count_chars($test3) as $test3char => &$test3charEmpty) { + if (\in_array($test3char, $str_chars, true)) { + ++$maybe_utf32be; + } + } + unset($test3charEmpty); + } + } + + if ($maybe_utf32be !== $maybe_utf32le) { + if ($maybe_utf32le > $maybe_utf32be) { + return 1; + } + + return 2; + } + + return false; + } + + /** + * Checks whether the passed input contains only byte sequences that appear valid UTF-8. + * + * EXAMPLE: + * UTF8::is_utf8(['Iñtërnâtiônàlizætiøn', 'foo']); // true + * // + * UTF8::is_utf8(["Iñtërnâtiônàlizætiøn\xA0\xA1", 'bar']); // false + * + * + * @param int|string|string[]|null $str

The input to be checked.

+ * @param bool $strict

Check also if the string is not UTF-16 or UTF-32.

+ * + * @psalm-pure + * + * @return bool + */ + public static function is_utf8($str, bool $strict = false): bool + { + if (\is_array($str)) { + foreach ($str as &$v) { + if (!self::is_utf8($v, $strict)) { + return false; + } + } + + return true; + } + + return self::is_utf8_string((string) $str, $strict); + } + + /** + * (PHP 5 >= 5.2.0, PECL json >= 1.2.0)
+ * Decodes a JSON string + * + * EXAMPLE: UTF8::json_decode('[1,"\u00a5","\u00e4"]'); // array(1, '¥', 'ä') + * + * @see http://php.net/manual/en/function.json-decode.php + * + * @param string $json

+ * The json string being decoded. + *

+ *

+ * This function only works with UTF-8 encoded strings. + *

+ *

PHP implements a superset of + * JSON - it will also encode and decode scalar types and NULL. The JSON standard + * only supports these values when they are nested inside an array or an object. + *

+ * @param bool $assoc [optional]

+ * When TRUE, returned objects will be converted into + * associative arrays. + *

+ * @param int $depth [optional]

+ * User specified recursion depth. + *

+ * @param int $options [optional]

+ * Bitmask of JSON decode options. Currently only + * JSON_BIGINT_AS_STRING + * is supported (default is to cast large integers as floats) + *

+ * + * @psalm-pure + * + * @return mixed + *

The value encoded in json in appropriate PHP type. Values true, false and + * null (case-insensitive) are returned as TRUE, FALSE and NULL respectively. + * NULL is returned if the json cannot be decoded or if the encoded data + * is deeper than the recursion limit.

+ */ + public static function json_decode( + string $json, + bool $assoc = false, + int $depth = 512, + int $options = 0 + ) { + $json = self::filter($json); + + if (self::$SUPPORT['json'] === false) { + throw new \RuntimeException('ext-json: is not installed'); + } + + if ($depth < 1) { + $depth = 1; + } + + return \json_decode($json, $assoc, $depth, $options); + } + + /** + * (PHP 5 >= 5.2.0, PECL json >= 1.2.0)
+ * Returns the JSON representation of a value. + * + * EXAMPLE: UTF8::json_encode(array(1, '¥', 'ä')); // '[1,"\u00a5","\u00e4"]' + * + * @see http://php.net/manual/en/function.json-encode.php + * + * @param mixed $value

+ * The value being encoded. Can be any type except + * a resource. + *

+ *

+ * All string data must be UTF-8 encoded. + *

+ *

PHP implements a superset of + * JSON - it will also encode and decode scalar types and NULL. The JSON standard + * only supports these values when they are nested inside an array or an object. + *

+ * @param int $options [optional]

+ * Bitmask consisting of JSON_HEX_QUOT, + * JSON_HEX_TAG, + * JSON_HEX_AMP, + * JSON_HEX_APOS, + * JSON_NUMERIC_CHECK, + * JSON_PRETTY_PRINT, + * JSON_UNESCAPED_SLASHES, + * JSON_FORCE_OBJECT, + * JSON_UNESCAPED_UNICODE. The behaviour of these + * constants is described on + * the JSON constants page. + *

+ * @param int $depth [optional]

+ * Set the maximum depth. Must be greater than zero. + *

+ * + * @psalm-pure + * + * @return false|string + *

A JSON encoded string on success or
+ * FALSE on failure.

+ */ + public static function json_encode($value, int $options = 0, int $depth = 512) + { + $value = self::filter($value); + + if (self::$SUPPORT['json'] === false) { + throw new \RuntimeException('ext-json: is not installed'); + } + + if ($depth < 1) { + $depth = 1; + } + + return \json_encode($value, $options, $depth); + } + + /** + * Checks whether JSON is available on the server. + * + * @psalm-pure + * + * @return bool + *

true if available, false otherwise

+ * + * @internal

Please do not use it anymore, we will make is private in next major version.

+ */ + public static function json_loaded(): bool + { + return \function_exists('json_decode'); + } + + /** + * Makes string's first char lowercase. + * + * EXAMPLE: UTF8::lcfirst('ÑTËRNÂTIÔNÀLIZÆTIØN'); // ñTËRNÂTIÔNÀLIZÆTIØN + * + * @param string $str

The input string

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * @param string|null $lang [optional]

Set the language for special cases: az, el, lt, + * tr

+ * @param bool $try_to_keep_the_string_length [optional]

true === try to keep the string length: e.g. ẞ + * -> ß

+ * + * @psalm-pure + * + * @return string + *

The resulting string.

+ */ + public static function lcfirst( + string $str, + string $encoding = 'UTF-8', + bool $clean_utf8 = false, + string $lang = null, + bool $try_to_keep_the_string_length = false + ): string { + if ($clean_utf8) { + $str = self::clean($str); + } + + $use_mb_functions = ($lang === null && !$try_to_keep_the_string_length); + + if ($encoding === 'UTF-8') { + $str_part_two = (string) \mb_substr($str, 1); + + if ($use_mb_functions) { + $str_part_one = \mb_strtolower( + (string) \mb_substr($str, 0, 1) + ); + } else { + $str_part_one = self::strtolower( + (string) \mb_substr($str, 0, 1), + $encoding, + false, + $lang, + $try_to_keep_the_string_length + ); + } + } else { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + $str_part_two = (string) self::substr($str, 1, null, $encoding); + + $str_part_one = self::strtolower( + (string) self::substr($str, 0, 1, $encoding), + $encoding, + false, + $lang, + $try_to_keep_the_string_length + ); + } + + return $str_part_one . $str_part_two; + } + + /** + * Lowercase for all words in the string. + * + * @param string $str

The input string.

+ * @param string[] $exceptions [optional]

Exclusion for some words.

+ * @param string $char_list [optional]

Additional chars that contains to words and do + * not start a new word.

+ * @param string $encoding [optional]

Set the charset.

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * @param string|null $lang [optional]

Set the language for special cases: az, el, lt, + * tr

+ * @param bool $try_to_keep_the_string_length [optional]

true === try to keep the string length: e.g. ẞ + * -> ß

+ * + * @psalm-pure + * + * @return string + */ + public static function lcwords( + string $str, + array $exceptions = [], + string $char_list = '', + string $encoding = 'UTF-8', + bool $clean_utf8 = false, + string $lang = null, + bool $try_to_keep_the_string_length = false + ): string { + if (!$str) { + return ''; + } + + $words = self::str_to_words($str, $char_list); + $use_exceptions = $exceptions !== []; + + $words_str = ''; + foreach ($words as &$word) { + if (!$word) { + continue; + } + + if ( + !$use_exceptions + || + !\in_array($word, $exceptions, true) + ) { + $words_str .= self::lcfirst($word, $encoding, $clean_utf8, $lang, $try_to_keep_the_string_length); + } else { + $words_str .= $word; + } + } + + return $words_str; + } + + /** + * Calculate Levenshtein distance between two strings. + * + * For better performance, in a real application with a single input string + * matched against many strings from a database, you will probably want to pre- + * encode the input only once and use \levenshtein(). + * + * Source: https://github.com/KEINOS/mb_levenshtein + * + * @see https://www.php.net/manual/en/function.levenshtein + * + * @param string $str1

One of the strings being evaluated for Levenshtein distance.

+ * @param string $str2

One of the strings being evaluated for Levenshtein distance.

+ * @param int $insertionCost [optional]

Defines the cost of insertion.

+ * @param int $replacementCost [optional]

Defines the cost of replacement.

+ * @param int $deletionCost [optional]

Defines the cost of deletion.

+ * + * @return int + */ + public static function levenshtein( + string $str1, + string $str2, + int $insertionCost = 1, + int $replacementCost = 1, + int $deletionCost = 1 + ): int { + $result = ASCII::to_ascii_remap($str1, $str2); + + return \levenshtein($result[0], $result[1], $insertionCost, $replacementCost, $deletionCost); + } + + /** + * Strip whitespace or other characters from the beginning of a UTF-8 string. + * + * EXAMPLE: UTF8::ltrim(' 中文空白  '); // '中文空白  ' + * + * @param string $str

The string to be trimmed

+ * @param string|null $chars

Optional characters to be stripped

+ * + * @psalm-pure + * + * @return string the string with unwanted characters stripped from the left + */ + public static function ltrim(string $str = '', string $chars = null): string + { + if ($str === '') { + return ''; + } + + if (self::$SUPPORT['mbstring'] === true) { + if ($chars !== null) { + /** @noinspection PregQuoteUsageInspection */ + $chars = \preg_quote($chars); + $pattern = "^[{$chars}]+"; + } else { + $pattern = '^[\\s]+'; + } + + return (string) \mb_ereg_replace($pattern, '', $str); + } + + if ($chars !== null) { + $chars = \preg_quote($chars, '/'); + $pattern = "^[{$chars}]+"; + } else { + $pattern = '^[\\s]+'; + } + + return self::regex_replace($str, $pattern, ''); + } + + /** + * Returns the UTF-8 character with the maximum code point in the given data. + * + * EXAMPLE: UTF8::max('abc-äöü-中文空白'); // 'ø' + * + * @param string|string[] $arg

A UTF-8 encoded string or an array of such strings.

+ * + * @psalm-pure + * + * @return string|null the character with the highest code point than others, returns null on failure or empty input + */ + public static function max($arg) + { + if (\is_array($arg)) { + $arg = \implode('', $arg); + } + + $codepoints = self::codepoints($arg); + if ($codepoints === []) { + return null; + } + + $codepoint_max = \max($codepoints); + + return self::chr((int) $codepoint_max); + } + + /** + * Calculates and returns the maximum number of bytes taken by any + * UTF-8 encoded character in the given string. + * + * EXAMPLE: UTF8::max_chr_width('Intërnâtiônàlizætiøn'); // 2 + * + * @param string $str

The original Unicode string.

+ * + * @psalm-pure + * + * @return int + *

Max byte lengths of the given chars.

+ * + * @phpstan-return 0|1|2|3|4 + */ + public static function max_chr_width(string $str): int + { + $bytes = self::chr_size_list($str); + if ($bytes !== []) { + return (int) \max($bytes); + } + + return 0; + } + + /** + * Checks whether mbstring is available on the server. + * + * @psalm-pure + * + * @return bool + *

true if available, false otherwise

+ * + * @internal

Please do not use it anymore, we will make is private in next major version.

+ */ + public static function mbstring_loaded(): bool + { + return \extension_loaded('mbstring'); + } + + /** + * Returns the UTF-8 character with the minimum code point in the given data. + * + * EXAMPLE: UTF8::min('abc-äöü-中文空白'); // '-' + * + * @param string|string[] $arg A UTF-8 encoded string or an array of such strings. + * + * @psalm-pure + * + * @return string|null + *

The character with the lowest code point than others, returns null on failure or empty input.

+ */ + public static function min($arg) + { + if (\is_array($arg)) { + $arg = \implode('', $arg); + } + + $codepoints = self::codepoints($arg); + if ($codepoints === []) { + return null; + } + + $codepoint_min = \min($codepoints); + + return self::chr((int) $codepoint_min); + } + + /** + * Normalize the encoding-"name" input. + * + * EXAMPLE: UTF8::normalize_encoding('UTF8'); // 'UTF-8' + * + * @param mixed $encoding

e.g.: ISO, UTF8, WINDOWS-1251 etc.

+ * @param mixed $fallback

e.g.: UTF-8

+ * + * @psalm-pure + * + * @return mixed|string + *

e.g.: ISO-8859-1, UTF-8, WINDOWS-1251 etc.
Will return a empty string as fallback (by default)

+ * + * @template TNormalizeEncodingFallback + * @phpstan-param string|TNormalizeEncodingFallback $fallback + * @phpstan-return string|TNormalizeEncodingFallback + */ + public static function normalize_encoding($encoding, $fallback = '') + { + /** + * @psalm-suppress ImpureStaticVariable + * + * @var array + */ + static $STATIC_NORMALIZE_ENCODING_CACHE = []; + + // init + $encoding = (string) $encoding; + + if (!$encoding) { + return $fallback; + } + + if ( + $encoding === 'UTF-8' + || + $encoding === 'UTF8' + ) { + return 'UTF-8'; + } + + if ( + $encoding === '8BIT' + || + $encoding === 'BINARY' + ) { + return 'CP850'; + } + + if ( + $encoding === 'HTML' + || + $encoding === 'HTML-ENTITIES' + ) { + return 'HTML-ENTITIES'; + } + + if ( + $encoding === 'ISO' + || + $encoding === 'ISO-8859-1' + ) { + return 'ISO-8859-1'; + } + + // only a fallback, for non "strict_types" usage ... + if ($encoding === '1') { + return $fallback; + } + + if (isset($STATIC_NORMALIZE_ENCODING_CACHE[$encoding])) { + return $STATIC_NORMALIZE_ENCODING_CACHE[$encoding]; + } + + if (self::$ENCODINGS === null) { + self::$ENCODINGS = self::getData('encodings'); + } + + if (\in_array($encoding, self::$ENCODINGS, true)) { + $STATIC_NORMALIZE_ENCODING_CACHE[$encoding] = $encoding; + + return $encoding; + } + + $encoding_original = $encoding; + $encoding = \strtoupper($encoding); + $encoding_upper_helper = (string) \preg_replace('/[^a-zA-Z0-9]/u', '', $encoding); + + $equivalences = [ + 'ISO8859' => 'ISO-8859-1', + 'ISO88591' => 'ISO-8859-1', + 'ISO' => 'ISO-8859-1', + 'LATIN' => 'ISO-8859-1', + 'LATIN1' => 'ISO-8859-1', // Western European + 'ISO88592' => 'ISO-8859-2', + 'LATIN2' => 'ISO-8859-2', // Central European + 'ISO88593' => 'ISO-8859-3', + 'LATIN3' => 'ISO-8859-3', // Southern European + 'ISO88594' => 'ISO-8859-4', + 'LATIN4' => 'ISO-8859-4', // Northern European + 'ISO88595' => 'ISO-8859-5', + 'ISO88596' => 'ISO-8859-6', // Greek + 'ISO88597' => 'ISO-8859-7', + 'ISO88598' => 'ISO-8859-8', // Hebrew + 'ISO88599' => 'ISO-8859-9', + 'LATIN5' => 'ISO-8859-9', // Turkish + 'ISO885911' => 'ISO-8859-11', + 'TIS620' => 'ISO-8859-11', // Thai + 'ISO885910' => 'ISO-8859-10', + 'LATIN6' => 'ISO-8859-10', // Nordic + 'ISO885913' => 'ISO-8859-13', + 'LATIN7' => 'ISO-8859-13', // Baltic + 'ISO885914' => 'ISO-8859-14', + 'LATIN8' => 'ISO-8859-14', // Celtic + 'ISO885915' => 'ISO-8859-15', + 'LATIN9' => 'ISO-8859-15', // Western European (with some extra chars e.g. €) + 'ISO885916' => 'ISO-8859-16', + 'LATIN10' => 'ISO-8859-16', // Southeast European + 'CP1250' => 'WINDOWS-1250', + 'WIN1250' => 'WINDOWS-1250', + 'WINDOWS1250' => 'WINDOWS-1250', + 'CP1251' => 'WINDOWS-1251', + 'WIN1251' => 'WINDOWS-1251', + 'WINDOWS1251' => 'WINDOWS-1251', + 'CP1252' => 'WINDOWS-1252', + 'WIN1252' => 'WINDOWS-1252', + 'WINDOWS1252' => 'WINDOWS-1252', + 'CP1253' => 'WINDOWS-1253', + 'WIN1253' => 'WINDOWS-1253', + 'WINDOWS1253' => 'WINDOWS-1253', + 'CP1254' => 'WINDOWS-1254', + 'WIN1254' => 'WINDOWS-1254', + 'WINDOWS1254' => 'WINDOWS-1254', + 'CP1255' => 'WINDOWS-1255', + 'WIN1255' => 'WINDOWS-1255', + 'WINDOWS1255' => 'WINDOWS-1255', + 'CP1256' => 'WINDOWS-1256', + 'WIN1256' => 'WINDOWS-1256', + 'WINDOWS1256' => 'WINDOWS-1256', + 'CP1257' => 'WINDOWS-1257', + 'WIN1257' => 'WINDOWS-1257', + 'WINDOWS1257' => 'WINDOWS-1257', + 'CP1258' => 'WINDOWS-1258', + 'WIN1258' => 'WINDOWS-1258', + 'WINDOWS1258' => 'WINDOWS-1258', + 'UTF16' => 'UTF-16', + 'UTF32' => 'UTF-32', + 'UTF8' => 'UTF-8', + 'UTF' => 'UTF-8', + 'UTF7' => 'UTF-7', + '8BIT' => 'CP850', + 'BINARY' => 'CP850', + ]; + + if (!empty($equivalences[$encoding_upper_helper])) { + $encoding = $equivalences[$encoding_upper_helper]; + } + + $STATIC_NORMALIZE_ENCODING_CACHE[$encoding_original] = $encoding; + + return $encoding; + } + + /** + * Standardize line ending to unix-like. + * + * @param string $str

The input string.

+ * @param string|string[] $replacer

The replacer char e.g. "\n" (Linux) or "\r\n" (Windows). You can also use \PHP_EOL + * here.

+ * + * @psalm-pure + * + * @return string + *

A string with normalized line ending.

+ */ + public static function normalize_line_ending(string $str, $replacer = "\n"): string + { + return \str_replace(["\r\n", "\r", "\n"], $replacer, $str); + } + + /** + * Normalize some MS Word special characters. + * + * EXAMPLE: UTF8::normalize_msword('„Abcdef…”'); // '"Abcdef..."' + * + * @param string $str

The string to be normalized.

+ * + * @psalm-pure + * + * @return string + *

A string with normalized characters for commonly used chars in Word documents.

+ */ + public static function normalize_msword(string $str): string + { + return ASCII::normalize_msword($str); + } + + /** + * Normalize the whitespace. + * + * EXAMPLE: UTF8::normalize_whitespace("abc-\xc2\xa0-öäü-\xe2\x80\xaf-\xE2\x80\xAC", true); // "abc-\xc2\xa0-öäü- -" + * + * @param string $str

The string to be normalized.

+ * @param bool $keep_non_breaking_space [optional]

Set to true, to keep non-breaking-spaces.

+ * @param bool $keep_bidi_unicode_controls [optional]

Set to true, to keep non-printable (for the web) + * bidirectional text chars.

+ * @param bool $normalize_control_characters [optional]

Set to true, to convert e.g. LINE-, PARAGRAPH-SEPARATOR with "\n" and LINE TABULATION with "\t".

+ * + * @psalm-pure + * + * @return string + *

A string with normalized whitespace.

+ */ + public static function normalize_whitespace( + string $str, + bool $keep_non_breaking_space = false, + bool $keep_bidi_unicode_controls = false, + bool $normalize_control_characters = false + ): string { + return ASCII::normalize_whitespace( + $str, + $keep_non_breaking_space, + $keep_bidi_unicode_controls, + $normalize_control_characters + ); + } + + /** + * Calculates Unicode code point of the given UTF-8 encoded character. + * + * INFO: opposite to UTF8::chr() + * + * EXAMPLE: UTF8::ord('☃'); // 0x2603 + * + * @param string $chr

The character of which to calculate code point.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return int + *

Unicode code point of the given character,
+ * 0 on invalid UTF-8 byte sequence

+ */ + public static function ord($chr, string $encoding = 'UTF-8'): int + { + /** + * @psalm-suppress ImpureStaticVariable + * + * @var array + */ + static $CHAR_CACHE = []; + + // init + $chr = (string) $chr; + + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + $cache_key = $chr . '_' . $encoding; + if (isset($CHAR_CACHE[$cache_key])) { + return $CHAR_CACHE[$cache_key]; + } + + // check again, if it's still not UTF-8 + if ($encoding !== 'UTF-8') { + $chr = self::encode($encoding, $chr); + } + + if (self::$ORD === null) { + self::$ORD = self::getData('ord'); + } + + if (isset(self::$ORD[$chr])) { + return $CHAR_CACHE[$cache_key] = self::$ORD[$chr]; + } + + // + // fallback via "IntlChar" + // + + if (self::$SUPPORT['intlChar'] === true) { + $code = \IntlChar::ord($chr); + if ($code) { + return $CHAR_CACHE[$cache_key] = $code; + } + } + + // + // fallback via vanilla php + // + + $chr = \unpack('C*', (string) \substr($chr, 0, 4)); + /** @noinspection PhpSillyAssignmentInspection - hack for phpstan */ + /** @var int[] $chr - "unpack": only false if the format string contains errors */ + $chr = $chr; + $code = $chr ? $chr[1] : 0; + + if ($code >= 0xF0 && isset($chr[4])) { + return $CHAR_CACHE[$cache_key] = ((($code - 0xF0) << 18) + (($chr[2] - 0x80) << 12) + (($chr[3] - 0x80) << 6) + $chr[4] - 0x80); + } + + if ($code >= 0xE0 && isset($chr[3])) { + return $CHAR_CACHE[$cache_key] = ((($code - 0xE0) << 12) + (($chr[2] - 0x80) << 6) + $chr[3] - 0x80); + } + + if ($code >= 0xC0 && isset($chr[2])) { + return $CHAR_CACHE[$cache_key] = ((($code - 0xC0) << 6) + $chr[2] - 0x80); + } + + return $CHAR_CACHE[$cache_key] = $code; + } + + /** + * Parses the string into an array (into the the second parameter). + * + * WARNING: Unlike "parse_str()", this method does not (re-)place variables in the current scope, + * if the second parameter is not set! + * + * EXAMPLE: + * UTF8::parse_str('Iñtërnâtiônéàlizætiøn=測試&arr[]=foo+測試&arr[]=ການທົດສອບ', $array); + * echo $array['Iñtërnâtiônéàlizætiøn']; // '測試' + * + * + * @see http://php.net/manual/en/function.parse-str.php + * + * @param string $str

The input string.

+ * @param array $result

The result will be returned into this reference parameter.

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * + * @psalm-pure + * + * @return bool + *

Will return false if php can't parse the string and we haven't any $result.

+ */ + public static function parse_str(string $str, &$result, bool $clean_utf8 = false): bool + { + if ($clean_utf8) { + $str = self::clean($str); + } + + if (self::$SUPPORT['mbstring'] === true) { + $return = \mb_parse_str($str, $result); + + return $return !== false && $result !== []; + } + + /** + * @psalm-suppress ImpureFunctionCall - we use the second parameter, so we don't change variables by magic + */ + \parse_str($str, $result); + + return $result !== []; + } + + /** + * Checks if \u modifier is available that enables Unicode support in PCRE. + * + * @psalm-pure + * + * @return bool + *

+ * true if support is available,
+ * false otherwise + *

+ */ + public static function pcre_utf8_support(): bool + { + /** @noinspection PhpUsageOfSilenceOperatorInspection */ + return (bool) @\preg_match('//u', ''); + } + + /** + * Create an array containing a range of UTF-8 characters. + * + * EXAMPLE: UTF8::range('κ', 'ζ'); // array('κ', 'ι', 'θ', 'η', 'ζ',) + * + * @param int|string $var1

Numeric or hexadecimal code points, or a UTF-8 character to start from.

+ * @param int|string $var2

Numeric or hexadecimal code points, or a UTF-8 character to end at.

+ * @param bool $use_ctype

use ctype to detect numeric and hexadecimal, otherwise we will use a simple + * "is_numeric"

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * @param float|int $step [optional]

+ * If a step value is given, it will be used as the + * increment between elements in the sequence. step + * should be given as a positive number. If not specified, + * step will default to 1. + *

+ * + * @psalm-pure + * + * @return list + */ + public static function range( + $var1, + $var2, + bool $use_ctype = true, + string $encoding = 'UTF-8', + $step = 1 + ): array { + if (!$var1 || !$var2) { + return []; + } + + if ($step !== 1) { + /** + * @psalm-suppress RedundantConditionGivenDocblockType + * @psalm-suppress DocblockTypeContradiction + * @phpstan-ignore-next-line | ignore wrong inputs + */ + if (!\is_numeric($step)) { + throw new \InvalidArgumentException('$step need to be a number, type given: ' . \gettype($step)); + } + + /** + * @psalm-suppress RedundantConditionGivenDocblockType - false-positive from psalm? + */ + if ($step <= 0) { + throw new \InvalidArgumentException('$step need to be a positive number, given: ' . $step); + } + } + + if ($use_ctype && self::$SUPPORT['ctype'] === false) { + throw new \RuntimeException('ext-ctype: is not installed'); + } + + $is_digit = false; + $is_xdigit = false; + + if ($use_ctype && \ctype_digit((string) $var1) && \ctype_digit((string) $var2)) { + $is_digit = true; + $start = (int) $var1; + } elseif ($use_ctype && \ctype_xdigit($var1) && \ctype_xdigit($var2)) { + $is_xdigit = true; + $start = (int) self::hex_to_int((string) $var1); + } elseif (!$use_ctype && \is_numeric($var1)) { + $start = (int) $var1; + } else { + $start = self::ord((string) $var1); + } + + if (!$start) { + return []; + } + + if ($is_digit) { + $end = (int) $var2; + } elseif ($is_xdigit) { + $end = (int) self::hex_to_int((string) $var2); + } elseif (!$use_ctype && \is_numeric($var2)) { + $end = (int) $var2; + } else { + $end = self::ord((string) $var2); + } + + if (!$end) { + return []; + } + + $array = []; + foreach (\range($start, $end, $step) as $i) { + $array[] = (string) self::chr((int) $i, $encoding); + } + + return $array; + } + + /** + * Get data from an array via array like string. + * + * EXAMPLE: $array['foo'][123] = 'lall'; UTF8::getUrlParamFromArray('foo[123]', $array); // 'lall' + * + * @param array $data + * + * @return mixed + */ + public static function getUrlParamFromArray(string $param, array $data) + { + /** + * @param array $searchArray + * @param array $array + * + * @return mixed + */ + $getUrlArgFromArrayHelper = static function (array $searchArray, array $array) use (&$getUrlArgFromArrayHelper) { + foreach ($searchArray as $key => $value) { + if (isset($array[$key])) { + if (\is_array($value) && \is_array($array[$key])) { + return $getUrlArgFromArrayHelper($value, $array[$key]); + } + + return $array[$key]; + } + } + + return null; + }; + + /** + * @param string $string + * @return array|null + */ + $getUrlKeyArgsFromString = static function (string $string) { + if (!self::str_contains($string, '?')) { + $string = '?' . $string; + } + + $args = parse_url($string, PHP_URL_QUERY); + if ($args) { + $query = []; + parse_str($args, $query); + + return $query; + } + + return null; + }; + + if (isset($data[$param])) { + return $data[$param]; + } + + $paramKeys = $getUrlKeyArgsFromString($param); + if ($paramKeys !== null) { + return $getUrlArgFromArrayHelper($paramKeys, $data); + } + + return null; + } + + /** + * Multi decode HTML entity + fix urlencoded-win1252-chars. + * + * EXAMPLE: UTF8::rawurldecode('tes%20öäü%20\u00edtest+test'); // 'tes öäü ítest+test' + * + * e.g: + * 'test+test' => 'test+test' + * 'Düsseldorf' => 'Düsseldorf' + * 'D%FCsseldorf' => 'Düsseldorf' + * 'Düsseldorf' => 'Düsseldorf' + * 'D%26%23xFC%3Bsseldorf' => 'Düsseldorf' + * 'Düsseldorf' => 'Düsseldorf' + * 'D%C3%BCsseldorf' => 'Düsseldorf' + * 'D%C3%83%C2%BCsseldorf' => 'Düsseldorf' + * 'D%25C3%2583%25C2%25BCsseldorf' => 'Düsseldorf' + * + * @param string $str

The input string.

+ * @param bool $multi_decode

Decode as often as possible.

+ * + * @psalm-pure + * + * @return string + *

The decoded URL, as a string.

+ * + * @template T as string + * @phpstan-param T $str + * @phpstan-return (T is non-empty-string ? non-empty-string : string) + */ + public static function rawurldecode(string $str, bool $multi_decode = true): string + { + if ($str === '') { + return ''; + } + + $str = self::urldecode_unicode_helper($str); + + if ($multi_decode) { + do { + $str_compare = $str; + + /** + * @psalm-suppress PossiblyInvalidArgument + */ + $str = \rawurldecode( + self::html_entity_decode( + self::to_utf8($str), + \ENT_QUOTES | \ENT_HTML5 + ) + ); + } while ($str_compare !== $str); + } else { + /** + * @psalm-suppress PossiblyInvalidArgument + */ + $str = \rawurldecode( + self::html_entity_decode( + self::to_utf8($str), + \ENT_QUOTES | \ENT_HTML5 + ) + ); + } + + return self::fix_simple_utf8($str); + } + + /** + * Replaces all occurrences of $pattern in $str by $replacement. + * + * @param string $str

The input string.

+ * @param string $pattern

The regular expression pattern.

+ * @param string $replacement

The string to replace with.

+ * @param string $options [optional]

Matching conditions to be used.

+ * @param string $delimiter [optional]

Delimiter the the regex. Default: '/'

+ * + * @psalm-pure + * + * @return string + */ + public static function regex_replace( + string $str, + string $pattern, + string $replacement, + string $options = '', + string $delimiter = '/' + ): string { + if ($options === 'msr') { + $options = 'ms'; + } + + // fallback + if (!$delimiter) { + $delimiter = '/'; + } + + return (string) \preg_replace( + $delimiter . $pattern . $delimiter . 'u' . $options, + $replacement, + $str + ); + } + + /** + * Remove the BOM from UTF-8 / UTF-16 / UTF-32 strings. + * + * EXAMPLE: UTF8::remove_bom("\xEF\xBB\xBFΜπορώ να"); // 'Μπορώ να' + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return string + *

A string without UTF-BOM.

+ */ + public static function remove_bom(string $str): string + { + if ($str === '') { + return ''; + } + + $str_length = \strlen($str); + foreach (self::$BOM as $bom_string => $bom_byte_length) { + if (\strncmp($str, $bom_string, $bom_byte_length) === 0) { + /** @var false|string $str_tmp - needed for PhpStan (stubs error) */ + $str_tmp = \substr($str, $bom_byte_length, $str_length); + if ($str_tmp === false) { + return ''; + } + + $str_length -= $bom_byte_length; + + $str = (string) $str_tmp; + } + } + + return $str; + } + + /** + * Removes duplicate occurrences of a string in another string. + * + * EXAMPLE: UTF8::remove_duplicates('öäü-κόσμεκόσμε-äöü', 'κόσμε'); // 'öäü-κόσμε-äöü' + * + * @param string $str

The base string.

+ * @param string|string[] $what

String to search for in the base string.

+ * + * @psalm-pure + * + * @return string + *

A string with removed duplicates.

+ */ + public static function remove_duplicates(string $str, $what = ' '): string + { + if (\is_string($what)) { + $what = [$what]; + } + + /** + * @psalm-suppress RedundantConditionGivenDocblockType + * @phpstan-ignore-next-line | ignore wrong inputs + */ + if (\is_array($what)) { + foreach ($what as $item) { + $str = (string) \preg_replace('/(' . \preg_quote($item, '/') . ')+/u', $item, $str); + } + } + + return $str; + } + + /** + * Remove html via "strip_tags()" from the string. + * + * @param string $str

The input string.

+ * @param string $allowable_tags [optional]

You can use the optional second parameter to specify tags which + * should not be stripped. Default: null + *

+ * + * @psalm-pure + * + * @return string + *

A string with without html tags.

+ */ + public static function remove_html(string $str, string $allowable_tags = ''): string + { + return \strip_tags($str, $allowable_tags); + } + + /** + * Remove all breaks [
| \r\n | \r | \n | ...] from the string. + * + * @param string $str

The input string.

+ * @param string $replacement [optional]

Default is a empty string.

+ * + * @psalm-pure + * + * @return string + *

A string without breaks.

+ */ + public static function remove_html_breaks(string $str, string $replacement = ''): string + { + return (string) \preg_replace("#/\r\n|\r|\n|#isU", $replacement, $str); + } + + /** + * Remove invisible characters from a string. + * + * e.g.: This prevents sandwiching null characters between ascii characters, like Java\0script. + * + * EXAMPLE: UTF8::remove_invisible_characters("κόσ\0με"); // 'κόσμε' + * + * copy&past from https://github.com/bcit-ci/CodeIgniter/blob/develop/system/core/Common.php + * + * @param string $str

The input string.

+ * @param bool $url_encoded [optional]

+ * Try to remove url encoded control character. + * WARNING: maybe contains false-positives e.g. aa%0Baa -> aaaa. + *
+ * Default: false + *

+ * @param string $replacement [optional]

The replacement character.

+ * @param bool $keep_basic_control_characters [optional]

Keep control characters like [LRM] or [LSEP].

+ * + * @psalm-pure + * + * @return string + *

A string without invisible chars.

+ */ + public static function remove_invisible_characters( + string $str, + bool $url_encoded = false, + string $replacement = '', + bool $keep_basic_control_characters = true + ): string { + return ASCII::remove_invisible_characters( + $str, + $url_encoded, + $replacement, + $keep_basic_control_characters + ); + } + + /** + * Returns a new string with the prefix $substring removed, if present. + * + * @param string $str

The input string.

+ * @param string $substring

The prefix to remove.

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * + * @psalm-pure + * + * @return string + *

A string without the prefix $substring.

+ */ + public static function remove_left( + string $str, + string $substring, + string $encoding = 'UTF-8' + ): string { + if ( + $substring + && + \strpos($str, $substring) === 0 + ) { + if ($encoding === 'UTF-8') { + return (string) \mb_substr( + $str, + (int) \mb_strlen($substring) + ); + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + return (string) self::substr( + $str, + (int) self::strlen($substring, $encoding), + null, + $encoding + ); + } + + return $str; + } + + /** + * Returns a new string with the suffix $substring removed, if present. + * + * @param string $str + * @param string $substring

The suffix to remove.

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * + * @psalm-pure + * + * @return string + *

A string having a $str without the suffix $substring.

+ */ + public static function remove_right( + string $str, + string $substring, + string $encoding = 'UTF-8' + ): string { + if ($substring && \substr($str, -\strlen($substring)) === $substring) { + if ($encoding === 'UTF-8') { + return (string) \mb_substr( + $str, + 0, + (int) \mb_strlen($str) - (int) \mb_strlen($substring) + ); + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + return (string) self::substr( + $str, + 0, + (int) self::strlen($str, $encoding) - (int) self::strlen($substring, $encoding), + $encoding + ); + } + + return $str; + } + + /** + * Returns a new string with the suffix $substring removed, if present and case-insensitive. + * + * @param string $str + * @param string $substring

The suffix to remove.

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * + * @psalm-pure + * + * @return string + *

A string having a $str without the suffix $substring.

+ */ + public static function remove_iright( + string $str, + string $substring, + string $encoding = 'UTF-8' + ): string { + if ($substring && self::strtoupper(\substr($str, -\strlen($substring)), $encoding) === self::strtoupper($substring, $encoding)) { + if ($encoding === 'UTF-8') { + return (string) \mb_substr( + $str, + 0, + (int) \mb_strlen($str) - (int) \mb_strlen($substring) + ); + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + return (string) self::substr( + $str, + 0, + (int) self::strlen($str, $encoding) - (int) self::strlen($substring, $encoding), + $encoding + ); + } + + return $str; + } + + /** + * Returns a new string with the prefix $substring removed, if present and case-insensitive. + * + * @param string $str

The input string.

+ * @param string $substring

The prefix to remove.

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * + * @psalm-pure + * + * @return string + *

A string without the prefix $substring.

+ */ + public static function remove_ileft( + string $str, + string $substring, + string $encoding = 'UTF-8' + ): string { + if ( + $substring + && + \strpos(self::strtoupper($str, $encoding), self::strtoupper($substring, $encoding)) === 0 + ) { + if ($encoding === 'UTF-8') { + return (string) \mb_substr( + $str, + (int) \mb_strlen($substring) + ); + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + return (string) self::substr( + $str, + (int) self::strlen($substring, $encoding), + null, + $encoding + ); + } + + return $str; + } + + /** + * Replaces all occurrences of $search in $str by $replacement. + * + * @param string $str

The input string.

+ * @param string $search

The needle to search for.

+ * @param string $replacement

The string to replace with.

+ * @param bool $case_sensitive [optional]

Whether or not to enforce case-sensitivity. Default: true

+ * + * @psalm-pure + * + * @return string + *

A string with replaced parts.

+ */ + public static function replace( + string $str, + string $search, + string $replacement, + bool $case_sensitive = true + ): string { + if ($case_sensitive) { + return \str_replace($search, $replacement, $str); + } + + return self::str_ireplace($search, $replacement, $str); + } + + /** + * Replaces all occurrences of $search in $str by $replacement. + * + * @param string $str

The input string.

+ * @param string[] $search

The elements to search for.

+ * @param string|string[] $replacement

The string to replace with.

+ * @param bool $case_sensitive [optional]

Whether or not to enforce case-sensitivity. Default: true

+ * + * @psalm-pure + * + * @return string + *

A string with replaced parts.

+ */ + public static function replace_all( + string $str, + array $search, + $replacement, + bool $case_sensitive = true + ): string { + if ($case_sensitive) { + return \str_replace($search, $replacement, $str); + } + + return self::str_ireplace($search, $replacement, $str); + } + + /** + * Replace the diamond question mark (�) and invalid-UTF8 chars with the replacement. + * + * EXAMPLE: UTF8::replace_diamond_question_mark('中文空白�', ''); // '中文空白' + * + * @param string $str

The input string

+ * @param string $replacement_char

The replacement character.

+ * @param bool $process_invalid_utf8_chars

Convert invalid UTF-8 chars

+ * + * @psalm-pure + * + * @return string + *

A string without diamond question marks (�).

+ */ + public static function replace_diamond_question_mark( + string $str, + string $replacement_char = '', + bool $process_invalid_utf8_chars = true + ): string { + if ($str === '') { + return ''; + } + + if ($process_invalid_utf8_chars) { + if ($replacement_char === '') { + $replacement_char_helper = 'none'; + } else { + $replacement_char_helper = \ord($replacement_char); + } + + if (self::$SUPPORT['mbstring'] === false) { + // if there is no native support for "mbstring", + // then we need to clean the string before ... + $str = self::clean($str); + } + + /** + * @psalm-suppress ImpureFunctionCall - we will reset the value in the next step + */ + $save = \mb_substitute_character(); + /** @noinspection PhpUsageOfSilenceOperatorInspection - ignore "Unknown character" warnings, it's working anyway */ + @\mb_substitute_character($replacement_char_helper); + // the polyfill maybe return false, so cast to string + $str = (string) \mb_convert_encoding($str, 'UTF-8', 'UTF-8'); + \mb_substitute_character($save); + } + + return \str_replace( + [ + "\xEF\xBF\xBD", + '�', + ], + [ + $replacement_char, + $replacement_char, + ], + $str + ); + } + + /** + * Strip whitespace or other characters from the end of a UTF-8 string. + * + * EXAMPLE: UTF8::rtrim('-ABC-中文空白- '); // '-ABC-中文空白-' + * + * @param string $str

The string to be trimmed.

+ * @param string|null $chars

Optional characters to be stripped.

+ * + * @psalm-pure + * + * @return string + *

A string with unwanted characters stripped from the right.

+ */ + public static function rtrim(string $str = '', string $chars = null): string + { + if ($str === '') { + return ''; + } + + if (self::$SUPPORT['mbstring'] === true) { + if ($chars !== null) { + /** @noinspection PregQuoteUsageInspection */ + $chars = \preg_quote($chars); + $pattern = "[{$chars}]+$"; + } else { + $pattern = '[\\s]+$'; + } + + return (string) \mb_ereg_replace($pattern, '', $str); + } + + if ($chars !== null) { + $chars = \preg_quote($chars, '/'); + $pattern = "[{$chars}]+$"; + } else { + $pattern = '[\\s]+$'; + } + + return self::regex_replace($str, $pattern, ''); + } + + /** + * WARNING: Print native UTF-8 support (libs) by default, e.g. for debugging. + * + * @param bool $useEcho + * + * @psalm-pure + * + * @return string|void + * + * @phpstan-return ($useEcho is true ? void : string) + */ + public static function showSupport(bool $useEcho = true) + { + // init + $html = ''; + + $html .= '
';
+        foreach (self::$SUPPORT as $key => &$value) {
+            $html .= $key . ' - ' . \print_r($value, true) . "\n
"; + } + $html .= '
'; + + if ($useEcho) { + echo $html; + } + + return $html; + } + + /** + * Converts a UTF-8 character to HTML Numbered Entity like "{". + * + * EXAMPLE: UTF8::single_chr_html_encode('κ'); // 'κ' + * + * @param string $char

The Unicode character to be encoded as numbered entity.

+ * @param bool $keep_ascii_chars

Set to true to keep ASCII chars. + * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + *

The HTML numbered entity for the given character.

+ * + * @template T as string + * @phpstan-param T $char + * @phpstan-return (T is non-empty-string ? non-empty-string : string) + */ + public static function single_chr_html_encode( + string $char, + bool $keep_ascii_chars = false, + string $encoding = 'UTF-8' + ): string { + if ($char === '') { + return ''; + } + + if ( + $keep_ascii_chars + && + ASCII::is_ascii($char) + ) { + return $char; + } + + return '&#' . self::ord($char, $encoding) . ';'; + } + + /** + * @param string $str + * @param int<1, max> $tab_length + * + * @psalm-pure + * + * @return string + * + * @template T as string + * @phpstan-param T $str + * @phpstan-return (T is non-empty-string ? non-empty-string : string) + */ + public static function spaces_to_tabs(string $str, int $tab_length = 4): string + { + if ($tab_length === 4) { + $tab = ' '; + } elseif ($tab_length === 2) { + $tab = ' '; + } else { + $tab = \str_repeat(' ', $tab_length); + } + + return \str_replace($tab, "\t", $str); + } + + /** + * Returns a camelCase version of the string. Trims surrounding spaces, + * capitalizes letters following digits, spaces, dashes and underscores, + * and removes spaces, dashes, as well as underscores. + * + * @param string $str

The input string.

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * @param string|null $lang [optional]

Set the language for special cases: az, el, lt, + * tr

+ * @param bool $try_to_keep_the_string_length [optional]

true === try to keep the string length: e.g. ẞ + * -> ß

+ * + * @psalm-pure + * + * @return string + */ + public static function str_camelize( + string $str, + string $encoding = 'UTF-8', + bool $clean_utf8 = false, + string $lang = null, + bool $try_to_keep_the_string_length = false + ): string { + if ($clean_utf8) { + $str = self::clean($str); + } + + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + $str = self::lcfirst( + \trim($str), + $encoding, + false, + $lang, + $try_to_keep_the_string_length + ); + $str = (string) \preg_replace('/^[-_]+/', '', $str); + + $use_mb_functions = $lang === null && !$try_to_keep_the_string_length; + + $str = (string) \preg_replace_callback( + '/[-_\\s]+(.)?/u', + /** + * @param array $match + * + * @psalm-pure + * + * @return string + */ + static function (array $match) use ($use_mb_functions, $encoding, $lang, $try_to_keep_the_string_length): string { + if (isset($match[1])) { + if ($use_mb_functions) { + if ($encoding === 'UTF-8') { + return \mb_strtoupper($match[1]); + } + + return \mb_strtoupper($match[1], $encoding); + } + + return self::strtoupper($match[1], $encoding, false, $lang, $try_to_keep_the_string_length); + } + + return ''; + }, + $str + ); + + return (string) \preg_replace_callback( + '/[\\p{N}]+(.)?/u', + /** + * @param array $match + * + * @psalm-pure + * + * @return string + */ + static function (array $match) use ($use_mb_functions, $encoding, $clean_utf8, $lang, $try_to_keep_the_string_length): string { + if ($use_mb_functions) { + if ($encoding === 'UTF-8') { + return \mb_strtoupper($match[0]); + } + + return \mb_strtoupper($match[0], $encoding); + } + + return self::strtoupper($match[0], $encoding, $clean_utf8, $lang, $try_to_keep_the_string_length); + }, + $str + ); + } + + /** + * Returns the string with the first letter of each word capitalized, + * except for when the word is a name which shouldn't be capitalized. + * + * @param string $str + * + * @psalm-pure + * + * @return string + *

A string with $str capitalized.

+ */ + public static function str_capitalize_name(string $str): string + { + return self::str_capitalize_name_helper( + self::str_capitalize_name_helper( + self::collapse_whitespace($str), + ' ' + ), + '-' + ); + } + + /** + * Returns true if the string contains $needle, false otherwise. By default + * the comparison is case-sensitive, but can be made insensitive by setting + * $case_sensitive to false. + * + * @param string $haystack

The input string.

+ * @param string $needle

Substring to look for.

+ * @param bool $case_sensitive [optional]

Whether or not to enforce case-sensitivity. Default: true

+ * + * @psalm-pure + * + * @return bool + *

Whether or not $haystack contains $needle.

+ */ + public static function str_contains( + string $haystack, + string $needle, + bool $case_sensitive = true + ): bool { + if ($case_sensitive) { + if (\PHP_VERSION_ID >= 80000) { + /** @phpstan-ignore-next-line - only for PHP8 */ + return \str_contains($haystack, $needle); + } + + return \strpos($haystack, $needle) !== false; + } + + return \mb_stripos($haystack, $needle) !== false; + } + + /** + * Returns true if the string contains all $needles, false otherwise. By + * default, the comparison is case-sensitive, but can be made insensitive by + * setting $case_sensitive to false. + * + * @param string $haystack

The input string.

+ * @param scalar[] $needles

SubStrings to look for.

+ * @param bool $case_sensitive [optional]

Whether or not to enforce case-sensitivity. Default: true

+ * + * @psalm-pure + * + * @return bool + *

Whether or not $haystack contains $needle.

+ */ + public static function str_contains_all( + string $haystack, + array $needles, + bool $case_sensitive = true + ): bool { + if ($haystack === '' || $needles === []) { + return false; + } + + foreach ($needles as &$needle) { + if ( + $case_sensitive + && + (!$needle || \strpos($haystack, (string)$needle) === false) + ) { + return false; + } + + if (!$needle || \mb_stripos($haystack, (string) $needle) === false) { + return false; + } + } + + return true; + } + + /** + * Returns true if the string contains any $needles, false otherwise. By + * default the comparison is case-sensitive, but can be made insensitive by + * setting $case_sensitive to false. + * + * @param string $haystack

The input string.

+ * @param scalar[] $needles

SubStrings to look for.

+ * @param bool $case_sensitive [optional]

Whether or not to enforce case-sensitivity. Default: true

+ * + * @psalm-pure + * + * @return bool + *

Whether or not $str contains $needle.

+ */ + public static function str_contains_any( + string $haystack, + array $needles, + bool $case_sensitive = true + ): bool { + if ($haystack === '' || $needles === []) { + return false; + } + + foreach ($needles as &$needle) { + if (!$needle) { + continue; + } + + if ($case_sensitive) { + if (\strpos($haystack, (string) $needle) !== false) { + return true; + } + + continue; + } + + if (\mb_stripos($haystack, (string) $needle) !== false) { + return true; + } + } + + return false; + } + + /** + * Returns a lowercase and trimmed string separated by dashes. Dashes are + * inserted before uppercase characters (with the exception of the first + * character of the string), and in place of spaces as well as underscores. + * + * @param string $str

The input string.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + */ + public static function str_dasherize(string $str, string $encoding = 'UTF-8'): string + { + return self::str_delimit($str, '-', $encoding); + } + + /** + * Returns a lowercase and trimmed string separated by the given delimiter. + * + * Delimiters are inserted before uppercase characters (with the exception + * of the first character of the string), and in place of spaces, dashes, + * and underscores. Alpha delimiters are not converted to lowercase. + * + * EXAMPLE: + * UTF8::str_delimit('test case, '#'); // 'test#case' + * UTF8::str_delimit('test -case', '**'); // 'test**case' + * + * + * @param string $str

The input string.

+ * @param string $delimiter

Sequence used to separate parts of the string.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * @param string|null $lang [optional]

Set the language for special cases: az, el, lt, + * tr

+ * @param bool $try_to_keep_the_string_length [optional]

true === try to keep the string length: e.g. ẞ -> + * ß

+ * + * @psalm-pure + * + * @return string + * + * @template T as string + * @phpstan-param T $str + * @phpstan-return (T is non-empty-string ? non-empty-string : string) + */ + public static function str_delimit( + string $str, + string $delimiter, + string $encoding = 'UTF-8', + bool $clean_utf8 = false, + string $lang = null, + bool $try_to_keep_the_string_length = false + ): string { + if (self::$SUPPORT['mbstring'] === true) { + $str = (string) \mb_ereg_replace('\\B(\\p{Lu})', '-\1', \trim($str)); + + $use_mb_functions = $lang === null && !$try_to_keep_the_string_length; + if ($use_mb_functions && $encoding === 'UTF-8') { + $str = \mb_strtolower($str); + } else { + $str = self::strtolower($str, $encoding, $clean_utf8, $lang, $try_to_keep_the_string_length); + } + + return (string) \mb_ereg_replace('[\\-_\\s]+', $delimiter, $str); + } + + $str = (string) \preg_replace('/\\B(\\p{Lu})/u', '-\1', \trim($str)); + + $use_mb_functions = $lang === null && !$try_to_keep_the_string_length; + if ($use_mb_functions && $encoding === 'UTF-8') { + $str = \mb_strtolower($str); + } else { + $str = self::strtolower($str, $encoding, $clean_utf8, $lang, $try_to_keep_the_string_length); + } + + return (string) \preg_replace('/[\\-_\\s]+/u', $delimiter, $str); + } + + /** + * Optimized "mb_detect_encoding()"-function -> with support for UTF-16 and UTF-32. + * + * EXAMPLE: + * UTF8::str_detect_encoding('中文空白'); // 'UTF-8' + * UTF8::str_detect_encoding('Abc'); // 'ASCII' + * + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return false|string + *

+ * The detected string-encoding e.g. UTF-8 or UTF-16BE,
+ * otherwise it will return false e.g. for BINARY or not detected encoding. + *

+ */ + public static function str_detect_encoding($str) + { + // init + $str = (string) $str; + + // + // 1.) check binary strings (010001001...) like UTF-16 / UTF-32 / PDF / Images / ... + // + + if (self::is_binary($str, self::string_has_bom($str) ? false : true)) { + $is_utf32 = self::is_utf32($str, false); + if ($is_utf32 === 1) { + return 'UTF-32LE'; + } + if ($is_utf32 === 2) { + return 'UTF-32BE'; + } + + $is_utf16 = self::is_utf16($str, false); + if ($is_utf16 === 1) { + return 'UTF-16LE'; + } + if ($is_utf16 === 2) { + return 'UTF-16BE'; + } + + // is binary but not "UTF-16" or "UTF-32" + return false; + } + + // + // 2.) simple check for ASCII chars + // + + if (ASCII::is_ascii($str)) { + return 'ASCII'; + } + + // + // 3.) simple check for UTF-8 chars + // + + if (self::is_utf8_string($str)) { + return 'UTF-8'; + } + + // + // 4.) check via "mb_detect_encoding()" + // + // INFO: UTF-16, UTF-32, UCS2 and UCS4, encoding detection will fail always with "mb_detect_encoding()" + + $encoding_detecting_order = [ + 'ISO-8859-1', + 'ISO-8859-2', + 'ISO-8859-3', + 'ISO-8859-4', + 'ISO-8859-5', + 'ISO-8859-6', + 'ISO-8859-7', + 'ISO-8859-8', + 'ISO-8859-9', + 'ISO-8859-10', + 'ISO-8859-13', + 'ISO-8859-14', + 'ISO-8859-15', + 'ISO-8859-16', + 'WINDOWS-1251', + 'WINDOWS-1252', + 'WINDOWS-1254', + 'CP932', + 'CP936', + 'CP950', + 'CP866', + 'CP850', + 'CP51932', + 'CP50220', + 'CP50221', + 'CP50222', + 'ISO-2022-JP', + 'ISO-2022-KR', + 'JIS', + 'JIS-ms', + 'EUC-CN', + 'EUC-JP', + ]; + + if (self::$SUPPORT['mbstring'] === true) { + // info: do not use the symfony polyfill here + $encoding = \mb_detect_encoding($str, $encoding_detecting_order, true); + if ($encoding) { + return $encoding; + } + } + + // + // 5.) check via "iconv()" + // + + if (self::$ENCODINGS === null) { + self::$ENCODINGS = self::getData('encodings'); + } + + foreach (self::$ENCODINGS as $encoding_tmp) { + // INFO: //IGNORE but still throw notice + /** @noinspection PhpUsageOfSilenceOperatorInspection */ + if ((string) @\iconv($encoding_tmp, $encoding_tmp . '//IGNORE', $str) === $str) { + return $encoding_tmp; + } + } + + return false; + } + + /** + * Check if the string ends with the given substring. + * + * EXAMPLE: + * UTF8::str_ends_with('BeginMiddleΚόσμε', 'Κόσμε'); // true + * UTF8::str_ends_with('BeginMiddleΚόσμε', 'κόσμε'); // false + * + * + * @param string $haystack

The string to search in.

+ * @param string $needle

The substring to search for.

+ * + * @psalm-pure + * + * @return bool + */ + public static function str_ends_with(string $haystack, string $needle): bool + { + if ($needle === '') { + return true; + } + + if ($haystack === '') { + return false; + } + + if (\PHP_VERSION_ID >= 80000) { + /** @phpstan-ignore-next-line - only for PHP8 */ + return \str_ends_with($haystack, $needle); + } + + return \substr($haystack, -\strlen($needle)) === $needle; + } + + /** + * Returns true if the string ends with any of $substrings, false otherwise. + * + * - case-sensitive + * + * @param string $str

The input string.

+ * @param string[] $substrings

Substrings to look for.

+ * + * @psalm-pure + * + * @return bool + *

Whether or not $str ends with $substring.

+ */ + public static function str_ends_with_any(string $str, array $substrings): bool + { + if ($substrings === []) { + return false; + } + + foreach ($substrings as &$substring) { + if (\substr($str, -\strlen($substring)) === $substring) { + return true; + } + } + + return false; + } + + /** + * Ensures that the string begins with $substring. If it doesn't, it's + * prepended. + * + * @param string $str

The input string.

+ * @param string $substring

The substring to add if not present.

+ * + * @psalm-pure + * + * @template T as string + * @template TSub as string + * @phpstan-param T $str + * @phpstan-param TSub $substring + * @phpstan-return (TSub is non-empty-string ? non-empty-string : (T is non-empty-string ? non-empty-string : string)) + */ + public static function str_ensure_left(string $str, string $substring): string + { + if ( + $substring !== '' + && + \strpos($str, $substring) === 0 + ) { + return $str; + } + + return $substring . $str; + } + + /** + * Ensures that the string ends with $substring. If it doesn't, it's appended. + * + * @param string $str

The input string.

+ * @param string $substring

The substring to add if not present.

+ * + * @psalm-pure + * + * @return string + * + * @template T as string + * @template TSub as string + * @phpstan-param T $str + * @phpstan-param TSub $substring + * @phpstan-return (TSub is non-empty-string ? non-empty-string : (T is non-empty-string ? non-empty-string : string)) + */ + public static function str_ensure_right(string $str, string $substring): string + { + if ( + $str === '' + || + $substring === '' + || + \substr($str, -\strlen($substring)) !== $substring + ) { + $str .= $substring; + } + + return $str; + } + + /** + * Capitalizes the first word of the string, replaces underscores with + * spaces, and strips '_id'. + * + * @param string $str + * + * @psalm-pure + * + * @return string + */ + public static function str_humanize($str): string + { + $str = \str_replace( + [ + '_id', + '_', + ], + [ + '', + ' ', + ], + $str + ); + + return self::ucfirst(\trim($str)); + } + + /** + * Check if the string ends with the given substring, case-insensitive. + * + * EXAMPLE: + * UTF8::str_iends_with('BeginMiddleΚόσμε', 'Κόσμε'); // true + * UTF8::str_iends_with('BeginMiddleΚόσμε', 'κόσμε'); // true + * + * + * @param string $haystack

The string to search in.

+ * @param string $needle

The substring to search for.

+ * + * @psalm-pure + * + * @return bool + */ + public static function str_iends_with(string $haystack, string $needle): bool + { + if ($needle === '') { + return true; + } + + if ($haystack === '') { + return false; + } + + return self::strcasecmp(\substr($haystack, -\strlen($needle)), $needle) === 0; + } + + /** + * Returns true if the string ends with any of $substrings, false otherwise. + * + * - case-insensitive + * + * @param string $str

The input string.

+ * @param string[] $substrings

Substrings to look for.

+ * + * @psalm-pure + * + * @return bool + *

Whether or not $str ends with $substring.

+ */ + public static function str_iends_with_any(string $str, array $substrings): bool + { + if ($substrings === []) { + return false; + } + + foreach ($substrings as &$substring) { + if (self::str_iends_with($str, $substring)) { + return true; + } + } + + return false; + } + + /** + * Inserts $substring into the string at the $index provided. + * + * @param string $str

The input string.

+ * @param string $substring

String to be inserted.

+ * @param int $index

The index at which to insert the substring.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + */ + public static function str_insert( + string $str, + string $substring, + int $index, + string $encoding = 'UTF-8' + ): string { + if ($encoding === 'UTF-8') { + $len = (int) \mb_strlen($str); + if ($index > $len) { + return $str; + } + + /** @noinspection UnnecessaryCastingInspection */ + return (string) \mb_substr($str, 0, $index) . + $substring . + (string) \mb_substr($str, $index, $len); + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + $len = (int) self::strlen($str, $encoding); + if ($index > $len) { + return $str; + } + + return ((string) self::substr($str, 0, $index, $encoding)) . + $substring . + ((string) self::substr($str, $index, $len, $encoding)); + } + + /** + * Case-insensitive and UTF-8 safe version of str_replace. + * + * EXAMPLE: + * UTF8::str_ireplace('lIzÆ', 'lise', 'Iñtërnâtiônàlizætiøn'); // 'Iñtërnâtiônàlisetiøn' + * + * + * @see http://php.net/manual/en/function.str-ireplace.php + * + * @param string|string[] $search

+ * Every replacement with search array is + * performed on the result of previous replacement. + *

+ * @param string|string[] $replacement

The replacement.

+ * @param string|string[] $subject

+ * If subject is an array, then the search and + * replace is performed with every entry of + * subject, and the return value is an array as + * well. + *

+ * @param int $count [optional]

+ * The number of matched and replaced needles will + * be returned in count which is passed by + * reference. + *

+ * + * @psalm-pure + * + * @return string|string[] + *

A string or an array of replacements.

+ * + * @template TStrIReplaceSubject + * @phpstan-param TStrIReplaceSubject $subject + * @phpstan-return TStrIReplaceSubject + */ + public static function str_ireplace($search, $replacement, $subject, &$count = null) + { + $search = (array) $search; + + /** @noinspection AlterInForeachInspection */ + foreach ($search as &$s) { + $s = (string) $s; + if ($s === '') { + $s = '/^(?<=.)$/'; + } else { + $s = '/' . \preg_quote($s, '/') . '/ui'; + } + } + + // fallback + /** @phpstan-ignore-next-line - only a fallback for PHP8 */ + if ($replacement === null) { + $replacement = ''; + } + /** @phpstan-ignore-next-line - only a fallback for PHP8 */ + if ($subject === null) { + $subject = ''; + } + + /** + * @psalm-suppress PossiblyNullArgument + * @phpstan-var TStrIReplaceSubject $subject + */ + $subject = \preg_replace($search, $replacement, $subject, -1, $count); + + return $subject; + } + + /** + * Replaces $search from the beginning of string with $replacement. + * + * @param string $str

The input string.

+ * @param string $search

The string to search for.

+ * @param string $replacement

The replacement.

+ * + * @psalm-pure + * + * @return string + *

The string after the replacement.

+ */ + public static function str_ireplace_beginning(string $str, string $search, string $replacement): string + { + if ($str === '') { + if ($replacement === '') { + return ''; + } + + if ($search === '') { + return $replacement; + } + } + + if ($search === '') { + return $str . $replacement; + } + + $searchLength = \strlen($search); + if (\strncasecmp($str, $search, $searchLength) === 0) { + return $replacement . \substr($str, $searchLength); + } + + return $str; + } + + /** + * Replaces $search from the ending of string with $replacement. + * + * @param string $str

The input string.

+ * @param string $search

The string to search for.

+ * @param string $replacement

The replacement.

+ * + * @psalm-pure + * + * @return string + *

The string after the replacement.

+ */ + public static function str_ireplace_ending(string $str, string $search, string $replacement): string + { + if ($str === '') { + if ($replacement === '') { + return ''; + } + + if ($search === '') { + return $replacement; + } + } + + if ($search === '') { + return $str . $replacement; + } + + if (\stripos($str, $search, \strlen($str) - \strlen($search)) !== false) { + $str = \substr($str, 0, -\strlen($search)) . $replacement; + } + + return $str; + } + + /** + * Check if the string starts with the given substring, case-insensitive. + * + * EXAMPLE: + * UTF8::str_istarts_with('ΚόσμεMiddleEnd', 'Κόσμε'); // true + * UTF8::str_istarts_with('ΚόσμεMiddleEnd', 'κόσμε'); // true + * + * + * @param string $haystack

The string to search in.

+ * @param string $needle

The substring to search for.

+ * + * @psalm-pure + * + * @return bool + */ + public static function str_istarts_with(string $haystack, string $needle): bool + { + if ($needle === '') { + return true; + } + + if ($haystack === '') { + return false; + } + + return self::stripos($haystack, $needle) === 0; + } + + /** + * Returns true if the string begins with any of $substrings, false otherwise. + * + * - case-insensitive + * + * @param string $str

The input string.

+ * @param scalar[] $substrings

Substrings to look for.

+ * + * @psalm-pure + * + * @return bool + *

Whether or not $str starts with $substring.

+ */ + public static function str_istarts_with_any(string $str, array $substrings): bool + { + if ($str === '') { + return false; + } + + if ($substrings === []) { + return false; + } + + foreach ($substrings as &$substring) { + if (self::str_istarts_with($str, (string) $substring)) { + return true; + } + } + + return false; + } + + /** + * Gets the substring after the first occurrence of a separator. + * + * @param string $str

The input string.

+ * @param string $separator

The string separator.

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * + * @psalm-pure + * + * @return string + */ + public static function str_isubstr_after_first_separator( + string $str, + string $separator, + string $encoding = 'UTF-8' + ): string { + if ($separator === '' || $str === '') { + return ''; + } + + $offset = self::stripos($str, $separator); + if ($offset === false) { + return ''; + } + + if ($encoding === 'UTF-8') { + return (string) \mb_substr( + $str, + $offset + (int) \mb_strlen($separator) + ); + } + + return (string) self::substr( + $str, + $offset + (int) self::strlen($separator, $encoding), + null, + $encoding + ); + } + + /** + * Gets the substring after the last occurrence of a separator. + * + * @param string $str

The input string.

+ * @param string $separator

The string separator.

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * + * @psalm-pure + * + * @return string + */ + public static function str_isubstr_after_last_separator( + string $str, + string $separator, + string $encoding = 'UTF-8' + ): string { + if ($separator === '' || $str === '') { + return ''; + } + + $offset = self::strripos($str, $separator); + if ($offset === false) { + return ''; + } + + if ($encoding === 'UTF-8') { + return (string) \mb_substr( + $str, + $offset + (int) self::strlen($separator) + ); + } + + return (string) self::substr( + $str, + $offset + (int) self::strlen($separator, $encoding), + null, + $encoding + ); + } + + /** + * Gets the substring before the first occurrence of a separator. + * + * @param string $str

The input string.

+ * @param string $separator

The string separator.

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * + * @psalm-pure + * + * @return string + */ + public static function str_isubstr_before_first_separator( + string $str, + string $separator, + string $encoding = 'UTF-8' + ): string { + if ($separator === '' || $str === '') { + return ''; + } + + $offset = self::stripos($str, $separator); + if ($offset === false) { + return ''; + } + + if ($encoding === 'UTF-8') { + return (string) \mb_substr($str, 0, $offset); + } + + return (string) self::substr($str, 0, $offset, $encoding); + } + + /** + * Gets the substring before the last occurrence of a separator. + * + * @param string $str

The input string.

+ * @param string $separator

The string separator.

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * + * @psalm-pure + * + * @return string + */ + public static function str_isubstr_before_last_separator( + string $str, + string $separator, + string $encoding = 'UTF-8' + ): string { + if ($separator === '' || $str === '') { + return ''; + } + + if ($encoding === 'UTF-8') { + $offset = \mb_strripos($str, $separator); + if ($offset === false) { + return ''; + } + + return (string) \mb_substr($str, 0, $offset); + } + + $offset = self::strripos($str, $separator, 0, $encoding); + if ($offset === false) { + return ''; + } + + return (string) self::substr($str, 0, $offset, $encoding); + } + + /** + * Gets the substring after (or before via "$before_needle") the first occurrence of the "$needle". + * + * @param string $str

The input string.

+ * @param string $needle

The string to look for.

+ * @param bool $before_needle [optional]

Default: false

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * + * @psalm-pure + * + * @return string + */ + public static function str_isubstr_first( + string $str, + string $needle, + bool $before_needle = false, + string $encoding = 'UTF-8' + ): string { + if ( + $needle === '' + || + $str === '' + ) { + return ''; + } + + $part = self::stristr( + $str, + $needle, + $before_needle, + $encoding + ); + if ($part === false) { + return ''; + } + + return $part; + } + + /** + * Gets the substring after (or before via "$before_needle") the last occurrence of the "$needle". + * + * @param string $str

The input string.

+ * @param string $needle

The string to look for.

+ * @param bool $before_needle [optional]

Default: false

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * + * @psalm-pure + * + * @return string + */ + public static function str_isubstr_last( + string $str, + string $needle, + bool $before_needle = false, + string $encoding = 'UTF-8' + ): string { + if ( + $needle === '' + || + $str === '' + ) { + return ''; + } + + $part = self::strrichr( + $str, + $needle, + $before_needle, + $encoding + ); + if ($part === false) { + return ''; + } + + return $part; + } + + /** + * Returns the last $n characters of the string. + * + * @param string $str

The input string.

+ * @param int $n

Number of characters to retrieve from the end.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + */ + public static function str_last_char( + string $str, + int $n = 1, + string $encoding = 'UTF-8' + ): string { + if ($str === '' || $n <= 0) { + return ''; + } + + if ($encoding === 'UTF-8') { + return (string) \mb_substr($str, -$n); + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + return (string) self::substr($str, -$n, null, $encoding); + } + + /** + * Limit the number of characters in a string. + * + * @param string $str

The input string.

+ * @param int<1, max> $length [optional]

Default: 100

+ * @param string $str_add_on [optional]

Default: …

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + * + * @template T as string + * @phpstan-param T $str + * @phpstan-return (T is non-empty-string ? non-empty-string : string) + */ + public static function str_limit( + string $str, + int $length = 100, + string $str_add_on = '…', + string $encoding = 'UTF-8' + ): string { + if ( + $str === '' + || + /* @phpstan-ignore-next-line | we do not trust the phpdoc check */ + $length <= 0 + ) { + return ''; + } + + if ($encoding === 'UTF-8') { + if ((int) \mb_strlen($str) <= $length) { + return $str; + } + + /** @noinspection UnnecessaryCastingInspection */ + return (string) \mb_substr($str, 0, $length - (int) self::strlen($str_add_on)) . $str_add_on; + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + if ((int) self::strlen($str, $encoding) <= $length) { + return $str; + } + + return ((string) self::substr($str, 0, $length - (int) self::strlen($str_add_on), $encoding)) . $str_add_on; + } + + /** + * Limit the number of characters in a string in bytes. + * + * @param string $str

The input string.

+ * @param int<1, max> $length [optional]

Default: 100

+ * @param string $str_add_on [optional]

Default: ...

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + * + * @template T as string + * @phpstan-param T $str + * @phpstan-return (T is non-empty-string ? non-empty-string : string) + */ + public static function str_limit_in_byte( + string $str, + int $length = 100, + string $str_add_on = '...', + string $encoding = 'UTF-8' + ): string { + if ( + $str === '' + || + /* @phpstan-ignore-next-line | we do not trust the phpdoc check */ + $length <= 0 + ) { + return ''; + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + if ((int) self::strlen_in_byte($str, $encoding) <= $length) { + return $str; + } + + return ((string) self::substr_in_byte($str, 0, $length - (int) self::strlen_in_byte($str_add_on), $encoding)) . $str_add_on; + } + + /** + * Limit the number of characters in a string, but also after the next word. + * + * EXAMPLE: UTF8::str_limit_after_word('fòô bàř fòô', 8, ''); // 'fòô bàř' + * + * @param string $str

The input string.

+ * @param int<1, max> $length [optional]

Default: 100

+ * @param string $str_add_on [optional]

Default: …

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + * + * @template T as string + * @phpstan-param T $str + * @phpstan-return (T is non-empty-string ? non-empty-string : string) + */ + public static function str_limit_after_word( + string $str, + int $length = 100, + string $str_add_on = '…', + string $encoding = 'UTF-8' + ): string { + if ( + $str === '' + || + /* @phpstan-ignore-next-line | we do not trust the phpdoc check */ + $length <= 0 + ) { + return ''; + } + + if ($encoding === 'UTF-8') { + if ((int) \mb_strlen($str) <= $length) { + return $str; + } + + if (\mb_substr($str, $length - 1, 1) === ' ') { + return ((string) \mb_substr($str, 0, $length - 1)) . $str_add_on; + } + + $str = \mb_substr($str, 0, $length); + + $array = \explode(' ', $str, -1); + $new_str = \implode(' ', $array); + + if ($new_str === '') { + return ((string) \mb_substr($str, 0, $length - 1)) . $str_add_on; + } + } else { + if ((int) self::strlen($str, $encoding) <= $length) { + return $str; + } + + if (self::substr($str, $length - 1, 1, $encoding) === ' ') { + return ((string) self::substr($str, 0, $length - 1, $encoding)) . $str_add_on; + } + + /** @noinspection CallableParameterUseCaseInTypeContextInspection - FP */ + $str = self::substr($str, 0, $length, $encoding); + if ($str === false) { + return '' . $str_add_on; + } + + $array = \explode(' ', $str, -1); + $new_str = \implode(' ', $array); + + if ($new_str === '') { + return ((string) self::substr($str, 0, $length - 1, $encoding)) . $str_add_on; + } + } + + return $new_str . $str_add_on; + } + + /** + * Returns the longest common prefix between the $str1 and $str2. + * + * @param string $str1

The input sting.

+ * @param string $str2

Second string for comparison.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + */ + public static function str_longest_common_prefix( + string $str1, + string $str2, + string $encoding = 'UTF-8' + ): string { + // init + $longest_common_prefix = ''; + + if ($encoding === 'UTF-8') { + $max_length = (int) \min( + \mb_strlen($str1), + \mb_strlen($str2) + ); + + for ($i = 0; $i < $max_length; ++$i) { + $char = \mb_substr($str1, $i, 1); + + if ( + $char !== false /* @phpstan-ignore-line | old polyfill will return false, or? */ + && + $char === \mb_substr($str2, $i, 1) + ) { + $longest_common_prefix .= $char; + } else { + break; + } + } + } else { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + $max_length = (int) \min( + self::strlen($str1, $encoding), + self::strlen($str2, $encoding) + ); + + for ($i = 0; $i < $max_length; ++$i) { + $char = self::substr($str1, $i, 1, $encoding); + + if ( + $char !== false + && + $char === self::substr($str2, $i, 1, $encoding) + ) { + $longest_common_prefix .= $char; + } else { + break; + } + } + } + + return $longest_common_prefix; + } + + /** + * Returns the longest common substring between the $str1 and $str2. + * In the case of ties, it returns that which occurs first. + * + * @param string $str1 + * @param string $str2

Second string for comparison.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + *

A string with its $str being the longest common substring.

+ */ + public static function str_longest_common_substring( + string $str1, + string $str2, + string $encoding = 'UTF-8' + ): string { + if ($str1 === '' || $str2 === '') { + return ''; + } + + // Uses dynamic programming to solve + // http://en.wikipedia.org/wiki/Longest_common_substring_problem + + if ($encoding === 'UTF-8') { + $str_length = (int) \mb_strlen($str1); + $other_length = (int) \mb_strlen($str2); + } else { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + $str_length = (int) self::strlen($str1, $encoding); + $other_length = (int) self::strlen($str2, $encoding); + } + + // Return if either string is empty + if ($str_length === 0 || $other_length === 0) { + return ''; + } + + $len = 0; + $end = 0; + $table = \array_fill( + 0, + $str_length + 1, + \array_fill(0, $other_length + 1, 0) + ); + + if ($encoding === 'UTF-8') { + for ($i = 1; $i <= $str_length; ++$i) { + for ($j = 1; $j <= $other_length; ++$j) { + $str_char = \mb_substr($str1, $i - 1, 1); + $other_char = \mb_substr($str2, $j - 1, 1); + + if ($str_char === $other_char) { + $table[$i][$j] = $table[$i - 1][$j - 1] + 1; + if ($table[$i][$j] > $len) { + $len = $table[$i][$j]; + $end = $i; + } + } else { + $table[$i][$j] = 0; + } + } + } + } else { + for ($i = 1; $i <= $str_length; ++$i) { + for ($j = 1; $j <= $other_length; ++$j) { + $str_char = self::substr($str1, $i - 1, 1, $encoding); + $other_char = self::substr($str2, $j - 1, 1, $encoding); + + if ($str_char === $other_char) { + $table[$i][$j] = $table[$i - 1][$j - 1] + 1; + if ($table[$i][$j] > $len) { + $len = $table[$i][$j]; + $end = $i; + } + } else { + $table[$i][$j] = 0; + } + } + } + } + + if ($encoding === 'UTF-8') { + return (string) \mb_substr($str1, $end - $len, $len); + } + + return (string) self::substr($str1, $end - $len, $len, $encoding); + } + + /** + * Returns the longest common suffix between the $str1 and $str2. + * + * @param string $str1 + * @param string $str2

Second string for comparison.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + */ + public static function str_longest_common_suffix( + string $str1, + string $str2, + string $encoding = 'UTF-8' + ): string { + if ($str1 === '' || $str2 === '') { + return ''; + } + + if ($encoding === 'UTF-8') { + $max_length = (int) \min( + \mb_strlen($str1, $encoding), + \mb_strlen($str2, $encoding) + ); + + $longest_common_suffix = ''; + for ($i = 1; $i <= $max_length; ++$i) { + $char = \mb_substr($str1, -$i, 1); + + if ( + $char !== false /* @phpstan-ignore-line | old polyfill will return false, or? */ + && + $char === \mb_substr($str2, -$i, 1) + ) { + $longest_common_suffix = $char . $longest_common_suffix; + } else { + break; + } + } + } else { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + $max_length = (int) \min( + self::strlen($str1, $encoding), + self::strlen($str2, $encoding) + ); + + $longest_common_suffix = ''; + for ($i = 1; $i <= $max_length; ++$i) { + $char = self::substr($str1, -$i, 1, $encoding); + + if ( + $char !== false + && + $char === self::substr($str2, -$i, 1, $encoding) + ) { + $longest_common_suffix = $char . $longest_common_suffix; + } else { + break; + } + } + } + + return $longest_common_suffix; + } + + /** + * Returns true if $str matches the supplied pattern, false otherwise. + * + * @param string $str

The input string.

+ * @param string $pattern

Regex pattern to match against.

+ * + * @psalm-pure + * + * @return bool + *

Whether or not $str matches the pattern.

+ */ + public static function str_matches_pattern(string $str, string $pattern): bool + { + return (bool) \preg_match('/' . $pattern . '/u', $str); + } + + /** + * Returns whether or not a character exists at an index. Offsets may be + * negative to count from the last character in the string. Implements + * part of the ArrayAccess interface. + * + * @param string $str

The input string.

+ * @param int $offset

The index to check.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return bool + *

Whether or not the index exists.

+ */ + public static function str_offset_exists(string $str, int $offset, string $encoding = 'UTF-8'): bool + { + // init + $length = (int) self::strlen($str, $encoding); + + if ($offset >= 0) { + return $length > $offset; + } + + return $length >= \abs($offset); + } + + /** + * Returns the character at the given index. Offsets may be negative to + * count from the last character in the string. Implements part of the + * ArrayAccess interface, and throws an OutOfBoundsException if the index + * does not exist. + * + * @param string $str

The input string.

+ * @param int<1, max> $index

The index from which to retrieve the char.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @throws \OutOfBoundsException if the positive or negative offset does not exist + * + * @return string + *

The character at the specified index.

+ * + * @psalm-pure + */ + public static function str_offset_get(string $str, int $index, string $encoding = 'UTF-8'): string + { + // init + $length = (int) self::strlen($str); + + if ( + /* @phpstan-ignore-next-line | we do not trust the phpdoc check */ + ($index >= 0 && $length <= $index) + || + $length < \abs($index) + ) { + throw new \OutOfBoundsException('No character exists at the index'); + } + + return self::char_at($str, $index, $encoding); + } + + /** + * Pad a UTF-8 string to a given length with another string. + * + * EXAMPLE: UTF8::str_pad('中文空白', 10, '_', STR_PAD_BOTH); // '___中文空白___' + * + * @param string $str

The input string.

+ * @param int $pad_length

The length of return string.

+ * @param string $pad_string [optional]

String to use for padding the input string.

+ * @param int|string $pad_type [optional]

+ * Can be STR_PAD_RIGHT (default), [or string "right"]
+ * STR_PAD_LEFT [or string "left"] or
+ * STR_PAD_BOTH [or string "both"] + *

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * + * @psalm-pure + * + * @return string + *

Returns the padded string.

+ */ + public static function str_pad( + string $str, + int $pad_length, + string $pad_string = ' ', + $pad_type = \STR_PAD_RIGHT, + string $encoding = 'UTF-8' + ): string { + if ($pad_length === 0 || $pad_string === '') { + return $str; + } + + if ($pad_type !== (int) $pad_type) { + if ($pad_type === 'left') { + $pad_type = \STR_PAD_LEFT; + } elseif ($pad_type === 'right') { + $pad_type = \STR_PAD_RIGHT; + } elseif ($pad_type === 'both') { + $pad_type = \STR_PAD_BOTH; + } else { + throw new \InvalidArgumentException( + 'Pad expects $pad_type to be "STR_PAD_*" or ' . "to be one of 'left', 'right' or 'both'" + ); + } + } + + if ($encoding === 'UTF-8') { + $str_length = (int) \mb_strlen($str); + + if ($pad_length >= $str_length) { + switch ($pad_type) { + case \STR_PAD_LEFT: + $ps_length = (int) \mb_strlen($pad_string); + + $diff = ($pad_length - $str_length); + + $pre = (string) \mb_substr( + \str_repeat($pad_string, (int) \ceil($diff / $ps_length)), + 0, + $diff + ); + $post = ''; + + break; + + case \STR_PAD_BOTH: + $diff = ($pad_length - $str_length); + + $ps_length_left = (int) \floor($diff / 2); + + $ps_length_right = (int) \ceil($diff / 2); + + $pre = (string) \mb_substr( + \str_repeat($pad_string, $ps_length_left), + 0, + $ps_length_left + ); + $post = (string) \mb_substr( + \str_repeat($pad_string, $ps_length_right), + 0, + $ps_length_right + ); + + break; + + case \STR_PAD_RIGHT: + default: + $ps_length = (int) \mb_strlen($pad_string); + + $diff = ($pad_length - $str_length); + + $post = (string) \mb_substr( + \str_repeat($pad_string, (int) \ceil($diff / $ps_length)), + 0, + $diff + ); + $pre = ''; + } + + return $pre . $str . $post; + } + + return $str; + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + $str_length = (int) self::strlen($str, $encoding); + + if ($pad_length >= $str_length) { + switch ($pad_type) { + case \STR_PAD_LEFT: + $ps_length = (int) self::strlen($pad_string, $encoding); + + $diff = ($pad_length - $str_length); + + $pre = (string) self::substr( + \str_repeat($pad_string, (int) \ceil($diff / $ps_length)), + 0, + $diff, + $encoding + ); + $post = ''; + + break; + + case \STR_PAD_BOTH: + $diff = ($pad_length - $str_length); + + $ps_length_left = (int) \floor($diff / 2); + + $ps_length_right = (int) \ceil($diff / 2); + + $pre = (string) self::substr( + \str_repeat($pad_string, $ps_length_left), + 0, + $ps_length_left, + $encoding + ); + $post = (string) self::substr( + \str_repeat($pad_string, $ps_length_right), + 0, + $ps_length_right, + $encoding + ); + + break; + + case \STR_PAD_RIGHT: + default: + $ps_length = (int) self::strlen($pad_string, $encoding); + + $diff = ($pad_length - $str_length); + + $post = (string) self::substr( + \str_repeat($pad_string, (int) \ceil($diff / $ps_length)), + 0, + $diff, + $encoding + ); + $pre = ''; + } + + return $pre . $str . $post; + } + + return $str; + } + + /** + * Returns a new string of a given length such that both sides of the + * string are padded. Alias for "UTF8::str_pad()" with a $pad_type of 'both'. + * + * @param string $str + * @param int $length

Desired string length after padding.

+ * @param string $pad_str [optional]

String used to pad, defaults to space. Default: ' '

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + *

The string with padding applied.

+ */ + public static function str_pad_both( + string $str, + int $length, + string $pad_str = ' ', + string $encoding = 'UTF-8' + ): string { + return self::str_pad( + $str, + $length, + $pad_str, + \STR_PAD_BOTH, + $encoding + ); + } + + /** + * Returns a new string of a given length such that the beginning of the + * string is padded. Alias for "UTF8::str_pad()" with a $pad_type of 'left'. + * + * @param string $str + * @param int $length

Desired string length after padding.

+ * @param string $pad_str [optional]

String used to pad, defaults to space. Default: ' '

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + *

The string with left padding.

+ */ + public static function str_pad_left( + string $str, + int $length, + string $pad_str = ' ', + string $encoding = 'UTF-8' + ): string { + return self::str_pad( + $str, + $length, + $pad_str, + \STR_PAD_LEFT, + $encoding + ); + } + + /** + * Returns a new string of a given length such that the end of the string + * is padded. Alias for "UTF8::str_pad()" with a $pad_type of 'right'. + * + * @param string $str + * @param int $length

Desired string length after padding.

+ * @param string $pad_str [optional]

String used to pad, defaults to space. Default: ' '

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + *

The string with right padding.

+ */ + public static function str_pad_right( + string $str, + int $length, + string $pad_str = ' ', + string $encoding = 'UTF-8' + ): string { + return self::str_pad( + $str, + $length, + $pad_str, + \STR_PAD_RIGHT, + $encoding + ); + } + + /** + * Repeat a string. + * + * EXAMPLE: UTF8::str_repeat("°~\xf0\x90\x28\xbc", 2); // '°~ð(¼°~ð(¼' + * + * @param string $str

+ * The string to be repeated. + *

+ * @param int<1, max> $multiplier

+ * Number of time the input string should be + * repeated. + *

+ *

+ * multiplier has to be greater than or equal to 0. + * If the multiplier is set to 0, the function + * will return an empty string. + *

+ * + * @psalm-pure + * + * @return string + *

The repeated string.

+ * + * @template T as string + * @phpstan-param T $str + * @phpstan-return (T is non-empty-string ? non-empty-string : string) + */ + public static function str_repeat(string $str, int $multiplier): string + { + $str = self::filter($str); + + return \str_repeat($str, $multiplier); + } + + /** + * INFO: This is only a wrapper for "str_replace()" -> the original functions is already UTF-8 safe. + * + * Replace all occurrences of the search string with the replacement string + * + * @see http://php.net/manual/en/function.str-replace.php + * + * @param string|string[] $search

+ * The value being searched for, otherwise known as the needle. + * An array may be used to designate multiple needles. + *

+ * @param string|string[] $replace

+ * The replacement value that replaces found search + * values. An array may be used to designate multiple replacements. + *

+ * @param string|string[] $subject

+ * The string or array of strings being searched and replaced on, + * otherwise known as the haystack. + *

+ *

+ * If subject is an array, then the search and + * replace is performed with every entry of + * subject, and the return value is an array as + * well. + *

+ * @param int|null $count [optional]

+ * If passed, this will hold the number of matched and replaced needles. + *

+ * + * @psalm-pure + * + * @return string|string[] + *

This function returns a string or an array with the replaced values.

+ * + * @template TStrReplaceSubject + * @phpstan-param TStrReplaceSubject $subject + * @phpstan-return TStrReplaceSubject + * + * @deprecated please use \str_replace() instead + */ + public static function str_replace( + $search, + $replace, + $subject, + int &$count = null + ) { + /** + * @psalm-suppress PossiblyNullArgument + * @phpstan-var TStrReplaceSubject $return; + */ + $return = \str_replace( + $search, + $replace, + $subject, + $count + ); + + return $return; + } + + /** + * Replaces $search from the beginning of string with $replacement. + * + * @param string $str

The input string.

+ * @param string $search

The string to search for.

+ * @param string $replacement

The replacement.

+ * + * @psalm-pure + * + * @return string + *

A string after the replacements.

+ */ + public static function str_replace_beginning( + string $str, + string $search, + string $replacement + ): string { + if ($str === '') { + if ($replacement === '') { + return ''; + } + + if ($search === '') { + return $replacement; + } + } + + if ($search === '') { + return $str . $replacement; + } + + $searchLength = \strlen($search); + if (\strncmp($str, $search, $searchLength) === 0) { + return $replacement . \substr($str, $searchLength); + } + + return $str; + } + + /** + * Replaces $search from the ending of string with $replacement. + * + * @param string $str

The input string.

+ * @param string $search

The string to search for.

+ * @param string $replacement

The replacement.

+ * + * @psalm-pure + * + * @return string + *

A string after the replacements.

+ */ + public static function str_replace_ending( + string $str, + string $search, + string $replacement + ): string { + if ($str === '') { + if ($replacement === '') { + return ''; + } + + if ($search === '') { + return $replacement; + } + } + + if ($search === '') { + return $str . $replacement; + } + + if (\strpos($str, $search, \strlen($str) - \strlen($search)) !== false) { + $str = \substr($str, 0, -\strlen($search)) . $replacement; + } + + return $str; + } + + /** + * Replace the first "$search"-term with the "$replace"-term. + * + * @param string $search + * @param string $replace + * @param string $subject + * + * @psalm-pure + * + * @return string + * + * @psalm-suppress InvalidReturnType + */ + public static function str_replace_first( + string $search, + string $replace, + string $subject + ): string { + $pos = self::strpos($subject, $search); + + if ($pos !== false) { + /** + * @psalm-suppress InvalidReturnStatement + */ + return self::substr_replace( + $subject, + $replace, + $pos, + (int) self::strlen($search) + ); + } + + return $subject; + } + + /** + * Replace the last "$search"-term with the "$replace"-term. + * + * @param string $search + * @param string $replace + * @param string $subject + * + * @psalm-pure + * + * @return string + * + * @psalm-suppress InvalidReturnType + */ + public static function str_replace_last( + string $search, + string $replace, + string $subject + ): string { + $pos = self::strrpos($subject, $search); + if ($pos !== false) { + /** + * @psalm-suppress InvalidReturnStatement + */ + return self::substr_replace( + $subject, + $replace, + $pos, + (int) self::strlen($search) + ); + } + + return $subject; + } + + /** + * Shuffles all the characters in the string. + * + * INFO: uses random algorithm which is weak for cryptography purposes + * + * EXAMPLE: UTF8::str_shuffle('fòô bàř fòô'); // 'àòôřb ffòô ' + * + * @param string $str

The input string

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @return string + *

The shuffled string.

+ * + * @template T as string + * @phpstan-param T $str + * @phpstan-return (T is non-empty-string ? non-empty-string : string) + */ + public static function str_shuffle(string $str, string $encoding = 'UTF-8'): string + { + if ($encoding === 'UTF-8') { + $indexes = \range(0, (int) \mb_strlen($str) - 1); + \shuffle($indexes); + + // init + $shuffled_str = ''; + + foreach ($indexes as &$i) { + $tmp_sub_str = \mb_substr($str, $i, 1); + if ($tmp_sub_str !== false) { /* @phpstan-ignore-line | old polyfill will return false, or? */ + $shuffled_str .= $tmp_sub_str; + } + } + } else { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + $indexes = \range(0, (int) self::strlen($str, $encoding) - 1); + \shuffle($indexes); + + // init + $shuffled_str = ''; + + foreach ($indexes as &$i) { + $tmp_sub_str = self::substr($str, $i, 1, $encoding); + if ($tmp_sub_str !== false) { + $shuffled_str .= $tmp_sub_str; + } + } + } + + return $shuffled_str; + } + + /** + * Returns the substring beginning at $start, and up to, but not including + * the index specified by $end. If $end is omitted, the function extracts + * the remaining string. If $end is negative, it is computed from the end + * of the string. + * + * @param string $str + * @param int $start

Initial index from which to begin extraction.

+ * @param int|null $end [optional]

Index at which to end extraction. Default: null

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return false|string + *

The extracted substring.

If str is shorter than start + * characters long, FALSE will be returned. + */ + public static function str_slice( + string $str, + int $start, + int $end = null, + string $encoding = 'UTF-8' + ) { + if ($encoding === 'UTF-8') { + if ($end === null) { + $length = (int) \mb_strlen($str); + } elseif ($end >= 0 && $end <= $start) { + return ''; + } elseif ($end < 0) { + $length = (int) \mb_strlen($str) + $end - $start; + } else { + $length = $end - $start; + } + + return \mb_substr($str, $start, $length); + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + if ($end === null) { + $length = (int) self::strlen($str, $encoding); + } elseif ($end >= 0 && $end <= $start) { + return ''; + } elseif ($end < 0) { + $length = (int) self::strlen($str, $encoding) + $end - $start; + } else { + $length = $end - $start; + } + + return self::substr($str, $start, $length, $encoding); + } + + /** + * Convert a string to e.g.: "snake_case" + * + * @param string $str + * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + *

A string in snake_case.

+ */ + public static function str_snakeize(string $str, string $encoding = 'UTF-8'): string + { + if ($str === '') { + return ''; + } + + $str = \str_replace( + '-', + '_', + self::normalize_whitespace($str) + ); + + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + $str = (string) \preg_replace_callback( + '/([\\p{N}|\\p{Lu}])/u', + /** + * @param string[] $matches + * + * @psalm-pure + * + * @return string + */ + static function (array $matches) use ($encoding): string { + $match = $matches[1]; + $match_int = (int) $match; + + if ((string) $match_int === $match) { + return '_' . $match . '_'; + } + + if ($encoding === 'UTF-8') { + return '_' . \mb_strtolower($match); + } + + return '_' . self::strtolower($match, $encoding); + }, + $str + ); + + $str = (string) \preg_replace( + [ + '/\\s+/u', // convert spaces to "_" + '/^\\s+|\\s+$/u', // trim leading & trailing spaces + '/_+/', // remove double "_" + ], + [ + '_', + '', + '_', + ], + $str + ); + + return \trim(\trim($str, '_')); // trim leading & trailing "_" + whitespace + } + + /** + * Sort all characters according to code points. + * + * EXAMPLE: UTF8::str_sort(' -ABC-中文空白- '); // ' ---ABC中文白空' + * + * @param string $str

A UTF-8 string.

+ * @param bool $unique

Sort unique. If true, repeated characters are ignored.

+ * @param bool $desc

If true, will sort characters in reverse code point order.

+ * + * @psalm-pure + * + * @return string + *

A string of sorted characters.

+ */ + public static function str_sort(string $str, bool $unique = false, bool $desc = false): string + { + /** @var int[] $array */ + $array = self::codepoints($str); + + if ($unique) { + $array = \array_flip(\array_flip($array)); + } + + if ($desc) { + \arsort($array); + } else { + \asort($array); + } + + return self::string($array); + } + + /** + * Convert a string to an array of Unicode characters. + * + * EXAMPLE: + * UTF8::str_split_array(['中文空白', 'test'], 2); // [['中文', '空白'], ['te', 'st']] + * + * + * @param int[]|string[] $input

The string[] or int[] to split into array.

+ * @param int<1, max> $length [optional]

Max character length of each array + * element.

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the + * string.

+ * @param bool $try_to_use_mb_functions [optional]

Set to false, if you don't want to use + * "mb_substr"

+ * + * @psalm-pure + * + * @return list> + *

An array containing chunks of the input.

+ */ + public static function str_split_array( + array $input, + int $length = 1, + bool $clean_utf8 = false, + bool $try_to_use_mb_functions = true + ): array { + foreach ($input as &$v) { + $v = self::str_split( + $v, + $length, + $clean_utf8, + $try_to_use_mb_functions + ); + } + + /** @var list> $input */ + return $input; + } + + /** + * Convert a string to an array of unicode characters. + * + * EXAMPLE: UTF8::str_split('中文空白'); // array('中', '文', '空', '白') + * + * @param int|string $str

The string or int to split into array.

+ * @param int<1, max> $length [optional]

Max character length of each array + * element.

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the + * string.

+ * @param bool $try_to_use_mb_functions [optional]

Set to false, if you don't want to use + * "mb_substr"

+ * + * @psalm-pure + * + * @return list + *

An array containing chunks of chars from the input.

+ */ + public static function str_split( + $str, + int $length = 1, + bool $clean_utf8 = false, + bool $try_to_use_mb_functions = true + ): array { + /* @phpstan-ignore-next-line | we do not trust the phpdoc check */ + if ($length <= 0) { + return []; + } + + // this is only an old fallback + /** @noinspection PhpSillyAssignmentInspection - hack for phpstan */ + /** @var int|int[]|string|string[] $str */ + $str = $str; + if (\is_array($str)) { + /** @psalm-suppress InvalidReturnStatement */ + /** @phpstan-ignore-next-line - old code :/ */ + return self::str_split_array( + $str, + $length, + $clean_utf8, + $try_to_use_mb_functions + ); + } + + // init + $str = (string) $str; + + if ($str === '') { + return []; + } + + if ($clean_utf8) { + $str = self::clean($str); + } + + if ( + $try_to_use_mb_functions + && + self::$SUPPORT['mbstring'] === true + ) { + if (\function_exists('mb_str_split')) { + try { + /** + * @psalm-suppress ImpureFunctionCall - why? + */ + $return = \mb_str_split($str, $length); + } catch (\Error $e) { + // PHP >= 8.0 : mb_str_split() will now throw ValueError on error. Previously, mb_str_split() returned false instead. + $return = false; + } + if ($return !== false) { + return $return; + } + } + + $i_max = \mb_strlen($str); + if ($i_max <= 127) { + $ret = []; + for ($i = 0; $i < $i_max; ++$i) { + $ret[] = \mb_substr($str, $i, 1); + } + } else { + $return_array = []; + \preg_match_all('/./us', $str, $return_array); + $ret = $return_array[0] ?? []; + } + } elseif (self::$SUPPORT['pcre_utf8'] === true) { + $return_array = []; + \preg_match_all('/./us', $str, $return_array); + $ret = $return_array[0] ?? []; + } else { + + // fallback + + $ret = []; + $len = \strlen($str); + + for ($i = 0; $i < $len; ++$i) { + if (($str[$i] & "\x80") === "\x00") { + $ret[] = $str[$i]; + } elseif ( + isset($str[$i + 1]) + && + ($str[$i] & "\xE0") === "\xC0" + ) { + if (($str[$i + 1] & "\xC0") === "\x80") { + $ret[] = $str[$i] . $str[$i + 1]; + + ++$i; + } + } elseif ( + isset($str[$i + 2]) + && + ($str[$i] & "\xF0") === "\xE0" + ) { + if ( + ($str[$i + 1] & "\xC0") === "\x80" + && + ($str[$i + 2] & "\xC0") === "\x80" + ) { + $ret[] = $str[$i] . $str[$i + 1] . $str[$i + 2]; + + $i += 2; + } + } elseif ( + isset($str[$i + 3]) + && + ($str[$i] & "\xF8") === "\xF0" + ) { + if ( + ($str[$i + 1] & "\xC0") === "\x80" + && + ($str[$i + 2] & "\xC0") === "\x80" + && + ($str[$i + 3] & "\xC0") === "\x80" + ) { + $ret[] = $str[$i] . $str[$i + 1] . $str[$i + 2] . $str[$i + 3]; + + $i += 3; + } + } + } + } + + if ($length > 1) { + return \array_map( + static function (array $item): string { + /* @phpstan-ignore-next-line | "array_map + array_chunk" is not supported by phpstan?! */ + return \implode('', $item); + }, + \array_chunk($ret, $length) + ); + } + + if (isset($ret[0]) && $ret[0] === '') { + return []; + } + + return $ret; + } + + /** + * Splits the string with the provided regular expression, returning an + * array of strings. An optional integer $limit will truncate the + * results. + * + * @param string $str + * @param string $pattern

The regex with which to split the string.

+ * @param int $limit [optional]

Maximum number of results to return. Default: -1 === no limit

+ * + * @psalm-pure + * + * @return string[] + *

An array of strings.

+ */ + public static function str_split_pattern(string $str, string $pattern, int $limit = -1): array + { + if ($limit === 0) { + return []; + } + + if ($pattern === '') { + return [$str]; + } + + if (self::$SUPPORT['mbstring'] === true) { + if ($limit >= 0) { + $result_tmp = \mb_split($pattern, $str); + if ($result_tmp === false) { + return []; + } + + $result = []; + foreach ($result_tmp as $item_tmp) { + if ($limit === 0) { + break; + } + --$limit; + + $result[] = $item_tmp; + } + + return $result; + } + + $result = \mb_split($pattern, $str); + if ($result === false) { + return []; + } + + return $result; + } + + if ($limit > 0) { + ++$limit; + } else { + $limit = -1; + } + + $array = \preg_split('/' . \preg_quote($pattern, '/') . '/u', $str, $limit); + if ($array === false) { + return []; + } + + if ($limit > 0 && \count($array) === $limit) { + \array_pop($array); + } + + return $array; + } + + /** + * Check if the string starts with the given substring. + * + * EXAMPLE: + * UTF8::str_starts_with('ΚόσμεMiddleEnd', 'Κόσμε'); // true + * UTF8::str_starts_with('ΚόσμεMiddleEnd', 'κόσμε'); // false + * + * + * @param string $haystack

The string to search in.

+ * @param string $needle

The substring to search for.

+ * + * @psalm-pure + * + * @return bool + */ + public static function str_starts_with(string $haystack, string $needle): bool + { + if ($needle === '') { + return true; + } + + if ($haystack === '') { + return false; + } + + if (\PHP_VERSION_ID >= 80000) { + /** @phpstan-ignore-next-line - only for PHP8 */ + return \str_starts_with($haystack, $needle); + } + + return \strncmp($haystack, $needle, \strlen($needle)) === 0; + } + + /** + * Returns true if the string begins with any of $substrings, false otherwise. + * + * - case-sensitive + * + * @param string $str

The input string.

+ * @param scalar[] $substrings

Substrings to look for.

+ * + * @psalm-pure + * + * @return bool + *

Whether or not $str starts with $substring.

+ */ + public static function str_starts_with_any(string $str, array $substrings): bool + { + if ($str === '') { + return false; + } + + if ($substrings === []) { + return false; + } + + foreach ($substrings as &$substring) { + if (self::str_starts_with($str, (string) $substring)) { + return true; + } + } + + return false; + } + + /** + * Gets the substring after the first occurrence of a separator. + * + * @param string $str

The input string.

+ * @param string $separator

The string separator.

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * + * @psalm-pure + * + * @return string + */ + public static function str_substr_after_first_separator(string $str, string $separator, string $encoding = 'UTF-8'): string + { + if ($separator === '' || $str === '') { + return ''; + } + + if ($encoding === 'UTF-8') { + $offset = \mb_strpos($str, $separator); + if ($offset === false) { + return ''; + } + + return (string) \mb_substr( + $str, + $offset + (int) \mb_strlen($separator) + ); + } + + $offset = self::strpos($str, $separator, 0, $encoding); + if ($offset === false) { + return ''; + } + + return (string) \mb_substr( + $str, + $offset + (int) self::strlen($separator, $encoding), + null, + $encoding + ); + } + + /** + * Gets the substring after the last occurrence of a separator. + * + * @param string $str

The input string.

+ * @param string $separator

The string separator.

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * + * @psalm-pure + * + * @return string + */ + public static function str_substr_after_last_separator( + string $str, + string $separator, + string $encoding = 'UTF-8' + ): string { + if ($separator === '' || $str === '') { + return ''; + } + + if ($encoding === 'UTF-8') { + $offset = \mb_strrpos($str, $separator); + if ($offset === false) { + return ''; + } + + return (string) \mb_substr( + $str, + $offset + (int) \mb_strlen($separator) + ); + } + + $offset = self::strrpos($str, $separator, 0, $encoding); + if ($offset === false) { + return ''; + } + + return (string) self::substr( + $str, + $offset + (int) self::strlen($separator, $encoding), + null, + $encoding + ); + } + + /** + * Gets the substring before the first occurrence of a separator. + * + * @param string $str

The input string.

+ * @param string $separator

The string separator.

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * + * @psalm-pure + * + * @return string + */ + public static function str_substr_before_first_separator( + string $str, + string $separator, + string $encoding = 'UTF-8' + ): string { + if ($separator === '' || $str === '') { + return ''; + } + + if ($encoding === 'UTF-8') { + $offset = \mb_strpos($str, $separator); + if ($offset === false) { + return ''; + } + + return (string) \mb_substr( + $str, + 0, + $offset + ); + } + + $offset = self::strpos($str, $separator, 0, $encoding); + if ($offset === false) { + return ''; + } + + return (string) self::substr( + $str, + 0, + $offset, + $encoding + ); + } + + /** + * Gets the substring before the last occurrence of a separator. + * + * @param string $str

The input string.

+ * @param string $separator

The string separator.

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * + * @psalm-pure + * + * @return string + */ + public static function str_substr_before_last_separator(string $str, string $separator, string $encoding = 'UTF-8'): string + { + if ($separator === '' || $str === '') { + return ''; + } + + if ($encoding === 'UTF-8') { + $offset = \mb_strrpos($str, $separator); + if ($offset === false) { + return ''; + } + + return (string) \mb_substr( + $str, + 0, + $offset + ); + } + + $offset = self::strrpos($str, $separator, 0, $encoding); + if ($offset === false) { + return ''; + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + return (string) self::substr( + $str, + 0, + $offset, + $encoding + ); + } + + /** + * Gets the substring after (or before via "$before_needle") the first occurrence of the "$needle". + * + * @param string $str

The input string.

+ * @param string $needle

The string to look for.

+ * @param bool $before_needle [optional]

Default: false

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * + * @psalm-pure + * + * @return string + */ + public static function str_substr_first( + string $str, + string $needle, + bool $before_needle = false, + string $encoding = 'UTF-8' + ): string { + if ($str === '' || $needle === '') { + return ''; + } + + if ($encoding === 'UTF-8') { + if ($before_needle) { + $part = \mb_strstr( + $str, + $needle, + $before_needle + ); + } else { + $part = \mb_strstr( + $str, + $needle + ); + } + } else { + $part = self::strstr( + $str, + $needle, + $before_needle, + $encoding + ); + } + + return $part === false ? '' : $part; + } + + /** + * Gets the substring after (or before via "$before_needle") the last occurrence of the "$needle". + * + * @param string $str

The input string.

+ * @param string $needle

The string to look for.

+ * @param bool $before_needle [optional]

Default: false

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * + * @psalm-pure + * + * @return string + */ + public static function str_substr_last( + string $str, + string $needle, + bool $before_needle = false, + string $encoding = 'UTF-8' + ): string { + if ($str === '' || $needle === '') { + return ''; + } + + if ($encoding === 'UTF-8') { + if ($before_needle) { + $part = \mb_strrchr( + $str, + $needle, + $before_needle + ); + } else { + $part = \mb_strrchr( + $str, + $needle + ); + } + } else { + $part = self::strrchr( + $str, + $needle, + $before_needle, + $encoding + ); + } + + return $part === false ? '' : $part; + } + + /** + * Surrounds $str with the given substring. + * + * @param string $str + * @param string $substring

The substring to add to both sides.

+ * + * @psalm-pure + * + * @return string + *

A string with the substring both prepended and appended.

+ * + * @template T as string + * @template TSub as string + * @phpstan-param T $str + * @phpstan-param TSub $substring + * @phpstan-return (T is non-empty-string ? non-empty-string : (TSub is non-empty-string ? non-empty-string : string)) + */ + public static function str_surround(string $str, string $substring): string + { + return $substring . $str . $substring; + } + + /** + * Returns a trimmed string with the first letter of each word capitalized. + * Also accepts an array, $ignore, allowing you to list words not to be + * capitalized. + * + * @param string $str + * @param string[]|null $ignore [optional]

An array of words not to capitalize or + * null. Default: null

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the + * string.

+ * @param string|null $lang [optional]

Set the language for special cases: az, + * el, lt, tr

+ * @param bool $try_to_keep_the_string_length [optional]

true === try to keep the string length: + * e.g. ẞ -> ß

+ * @param bool $use_trim_first [optional]

true === trim the input string, + * first

+ * @param string|null $word_define_chars [optional]

An string of chars that will be used as + * whitespace separator === words.

+ * + * @psalm-pure + * + * @return string + *

The titleized string.

+ */ + public static function str_titleize( + string $str, + array $ignore = null, + string $encoding = 'UTF-8', + bool $clean_utf8 = false, + string $lang = null, + bool $try_to_keep_the_string_length = false, + bool $use_trim_first = true, + string $word_define_chars = null + ): string { + if ($str === '') { + return ''; + } + + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + if ($use_trim_first) { + $str = \trim($str); + } + + if ($clean_utf8) { + $str = self::clean($str); + } + + $use_mb_functions = $lang === null && !$try_to_keep_the_string_length; + + if ($word_define_chars) { + $word_define_chars = \preg_quote($word_define_chars, '/'); + } else { + $word_define_chars = ''; + } + + $str = (string) \preg_replace_callback( + '/([^\\s' . $word_define_chars . ']+)/u', + static function (array $match) use ($try_to_keep_the_string_length, $lang, $ignore, $use_mb_functions, $encoding): string { + if ($ignore !== null && \in_array($match[0], $ignore, true)) { + return $match[0]; + } + + if ($use_mb_functions) { + if ($encoding === 'UTF-8') { + return \mb_strtoupper(\mb_substr($match[0], 0, 1)) + . \mb_strtolower(\mb_substr($match[0], 1)); + } + + return \mb_strtoupper(\mb_substr($match[0], 0, 1, $encoding), $encoding) + . \mb_strtolower(\mb_substr($match[0], 1, null, $encoding), $encoding); + } + + return self::ucfirst( + self::strtolower( + $match[0], + $encoding, + false, + $lang, + $try_to_keep_the_string_length + ), + $encoding, + false, + $lang, + $try_to_keep_the_string_length + ); + }, + $str + ); + + return $str; + } + + /** + * Convert a string into a obfuscate string. + * + * EXAMPLE: + * + * UTF8::str_obfuscate('lars@moelleken.org', 0.5, '*', ['@', '.']); // e.g. "l***@m**lleke*.*r*" + * + * + * @param string $str + * @param float $percent + * @param string $obfuscateChar + * @param string[] $keepChars + * + * @psalm-pure + * + * @return string + *

The obfuscate string.

+ */ + public static function str_obfuscate( + string $str, + float $percent = 0.5, + string $obfuscateChar = '*', + array $keepChars = [] + ): string { + $obfuscateCharHelper = "\u{2603}"; + $str = \str_replace($obfuscateChar, $obfuscateCharHelper, $str); + + $chars = self::chars($str); + $charsMax = \count($chars); + $charsMaxChange = \round($charsMax * $percent); + $charsCounter = 0; + $charKeyDone = []; + + while ($charsCounter < $charsMaxChange) { + foreach ($chars as $charKey => $char) { + if (isset($charKeyDone[$charKey])) { + continue; + } + + if (\random_int(0, 100) > 50) { + continue; + } + + if ($char === $obfuscateChar) { + continue; + } + + ++$charsCounter; + $charKeyDone[$charKey] = true; + + if ($charsCounter > $charsMaxChange) { + break; + } + + if (\in_array($char, $keepChars, true)) { + continue; + } + + $chars[$charKey] = $obfuscateChar; + } + } + + $str = \implode('', $chars); + + return \str_replace($obfuscateCharHelper, $obfuscateChar, $str); + } + + /** + * Returns a trimmed string in proper title case. + * + * Also accepts an array, $ignore, allowing you to list words not to be + * capitalized. + * + * Adapted from John Gruber's script. + * + * @see https://gist.github.com/gruber/9f9e8650d68b13ce4d78 + * + * @param string $str + * @param string[] $ignore

An array of words not to capitalize.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + *

The titleized string.

+ */ + public static function str_titleize_for_humans( + string $str, + array $ignore = [], + string $encoding = 'UTF-8' + ): string { + if ($str === '') { + return ''; + } + + $small_words = [ + '(? In-Flight + $str = (string) \preg_replace_callback( + '~\\b + (? "Stand-In" (Stand is already capped at this point) + $str = (string) \preg_replace_callback( + '~\\b + (?UTF8::str_to_binary('😃'); // '11110000100111111001100010000011' + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return false|string + *

false on error

+ */ + public static function str_to_binary(string $str) + { + $value = \unpack('H*', $str); + if ($value === false) { + return false; + } + + return \base_convert($value[1], 16, 2); + } + + /** + * @param string $str + * @param bool $remove_empty_values

Remove empty values.

+ * @param int|null $remove_short_values

The min. string length or null to disable

+ * + * @psalm-pure + * + * @return string[] + */ + public static function str_to_lines(string $str, bool $remove_empty_values = false, int $remove_short_values = null): array + { + if ($str === '') { + return $remove_empty_values ? [] : ['']; + } + + if (self::$SUPPORT['mbstring'] === true) { + $return = \mb_split("[\r\n]{1,2}", $str); + } else { + $return = \preg_split("/[\r\n]{1,2}/u", $str); + } + + if ($return === false) { + return $remove_empty_values ? [] : ['']; + } + + if ( + $remove_short_values === null + && + !$remove_empty_values + ) { + return $return; + } + + return self::reduce_string_array( + $return, + $remove_empty_values, + $remove_short_values + ); + } + + /** + * Convert a string into an array of words. + * + * EXAMPLE: UTF8::str_to_words('中文空白 oöäü#s', '#') // array('', '中文空白', ' ', 'oöäü#s', '') + * + * @param string $str + * @param string $char_list

Additional chars for the definition of "words".

+ * @param bool $remove_empty_values

Remove empty values.

+ * @param int|null $remove_short_values

The min. string length or null to disable

+ * + * @psalm-pure + * + * @return list + * + * @phpstan-return ($remove_empty_values is true ? list : non-empty-list) + */ + public static function str_to_words( + string $str, + string $char_list = '', + bool $remove_empty_values = false, + int $remove_short_values = null + ): array { + if ($str === '') { + return $remove_empty_values ? [] : ['']; + } + + $char_list = self::rxClass($char_list, '\pL'); + + $return = \preg_split("/({$char_list}+(?:[\p{Pd}’']{$char_list}+)*)/u", $str, -1, \PREG_SPLIT_DELIM_CAPTURE); + if ($return === false) { + return $remove_empty_values ? [] : ['']; + } + + if ( + $remove_short_values === null + && + !$remove_empty_values + ) { + return $return; + } + + $tmp_return = self::reduce_string_array( + $return, + $remove_empty_values, + $remove_short_values + ); + + foreach ($tmp_return as &$item) { + $item = (string) $item; + } + + return $tmp_return; + } + + /** + * Truncates the string to a given length. If $substring is provided, and + * truncating occurs, the string is further truncated so that the substring + * may be appended without exceeding the desired length. + * + * @param string $str + * @param int $length

Desired length of the truncated string.

+ * @param string $substring [optional]

The substring to append if it can fit. Default: ''

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * + * @psalm-pure + * + * @return string + *

A string after truncating.

+ */ + public static function str_truncate( + string $str, + int $length, + string $substring = '', + string $encoding = 'UTF-8' + ): string { + if ($str === '') { + return ''; + } + + if ($encoding === 'UTF-8') { + if ($length >= (int) \mb_strlen($str)) { + return $str; + } + + if ($substring !== '') { + $length -= (int) \mb_strlen($substring); + + /** @noinspection UnnecessaryCastingInspection */ + return (string) \mb_substr($str, 0, $length) . $substring; + } + + return (string) \mb_substr($str, 0, $length); + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + if ($length >= (int) self::strlen($str, $encoding)) { + return $str; + } + + if ($substring !== '') { + $length -= (int) self::strlen($substring, $encoding); + } + + return ( + (string) self::substr( + $str, + 0, + $length, + $encoding + ) + ) . $substring; + } + + /** + * Truncates the string to a given length, while ensuring that it does not + * split words. If $substring is provided, and truncating occurs, the + * string is further truncated so that the substring may be appended without + * exceeding the desired length. + * + * @param string $str + * @param int $length

Desired length of the truncated string.

+ * @param string $substring [optional]

The substring to append if it can fit. + * Default: + * ''

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * @param bool $ignore_do_not_split_words_for_one_word [optional]

Default: false

+ * + * @psalm-pure + * + * @return string + *

A string after truncating.

+ */ + public static function str_truncate_safe( + string $str, + int $length, + string $substring = '', + string $encoding = 'UTF-8', + bool $ignore_do_not_split_words_for_one_word = false + ): string { + if ($str === '' || $length <= 0) { + return $substring; + } + + if ($encoding === 'UTF-8') { + if ($length >= (int) \mb_strlen($str)) { + return $str; + } + + // need to further trim the string so we can append the substring + $length -= (int) \mb_strlen($substring); + if ($length <= 0) { + return $substring; + } + + /** @var false|string $truncated - needed for PhpStan (stubs error) */ + $truncated = \mb_substr($str, 0, $length); + if ($truncated === false) { + return ''; + } + + // if the last word was truncated + $space_position = \mb_strpos($str, ' ', $length - 1); + if ($space_position !== $length) { + // find pos of the last occurrence of a space, get up to that + $last_position = \mb_strrpos($truncated, ' ', 0); + + if ( + $last_position !== false + || + ( + $space_position !== false + && + !$ignore_do_not_split_words_for_one_word + ) + ) { + $truncated = (string) \mb_substr($truncated, 0, (int) $last_position); + } + } + } else { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + if ($length >= (int) self::strlen($str, $encoding)) { + return $str; + } + + // need to further trim the string so we can append the substring + $length -= (int) self::strlen($substring, $encoding); + if ($length <= 0) { + return $substring; + } + + $truncated = self::substr($str, 0, $length, $encoding); + + if ($truncated === false) { + return ''; + } + + // if the last word was truncated + $space_position = self::strpos($str, ' ', $length - 1, $encoding); + if ($space_position !== $length) { + // find pos of the last occurrence of a space, get up to that + $last_position = self::strrpos($truncated, ' ', 0, $encoding); + + if ( + $last_position !== false + || + ( + $space_position !== false + && + !$ignore_do_not_split_words_for_one_word + ) + ) { + $truncated = (string) self::substr($truncated, 0, (int) $last_position, $encoding); + } + } + } + + return $truncated . $substring; + } + + /** + * Returns a lowercase and trimmed string separated by underscores. + * Underscores are inserted before uppercase characters (with the exception + * of the first character of the string), and in place of spaces as well as + * dashes. + * + * @param string $str + * + * @psalm-pure + * + * @return string + *

The underscored string.

+ */ + public static function str_underscored(string $str): string + { + return self::str_delimit($str, '_'); + } + + /** + * Returns an UpperCamelCase version of the supplied string. It trims + * surrounding spaces, capitalizes letters following digits, spaces, dashes + * and underscores, and removes spaces, dashes, underscores. + * + * @param string $str

The input string.

+ * @param string $encoding [optional]

Default: 'UTF-8'

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * @param string|null $lang [optional]

Set the language for special cases: az, el, lt, + * tr

+ * @param bool $try_to_keep_the_string_length [optional]

true === try to keep the string length: e.g. ẞ + * -> ß

+ * + * @psalm-pure + * + * @return string + *

A string in UpperCamelCase.

+ */ + public static function str_upper_camelize( + string $str, + string $encoding = 'UTF-8', + bool $clean_utf8 = false, + string $lang = null, + bool $try_to_keep_the_string_length = false + ): string { + return self::ucfirst(self::str_camelize($str, $encoding), $encoding, $clean_utf8, $lang, $try_to_keep_the_string_length); + } + + /** + * Get the number of words in a specific string. + * + * EXAMPLES: + * // format: 0 -> return only word count (int) + * // + * UTF8::str_word_count('中文空白 öäü abc#c'); // 4 + * UTF8::str_word_count('中文空白 öäü abc#c', 0, '#'); // 3 + * + * // format: 1 -> return words (array) + * // + * UTF8::str_word_count('中文空白 öäü abc#c', 1); // array('中文空白', 'öäü', 'abc', 'c') + * UTF8::str_word_count('中文空白 öäü abc#c', 1, '#'); // array('中文空白', 'öäü', 'abc#c') + * + * // format: 2 -> return words with offset (array) + * // + * UTF8::str_word_count('中文空白 öäü ab#c', 2); // array(0 => '中文空白', 5 => 'öäü', 9 => 'abc', 13 => 'c') + * UTF8::str_word_count('中文空白 öäü ab#c', 2, '#'); // array(0 => '中文空白', 5 => 'öäü', 9 => 'abc#c') + * + * + * @param string $str

The input string.

+ * @param int $format [optional]

+ * 0 => return a number of words (default)
+ * 1 => return an array of words
+ * 2 => return an array of words with word-offset as key + *

+ * @param string $char_list [optional]

Additional chars that contains to words and do not start a new word.

+ * + * @psalm-pure + * + * @return int|string[] + *

The number of words in the string.

+ * + * @phpstan-param 0|1|2 $format + * @phpstan-return ($format is 2 ? array : ($format is 1 ? list : 0|positive-int)) + */ + public static function str_word_count(string $str, int $format = 0, string $char_list = '') + { + $str_parts = self::str_to_words($str, $char_list); + + $len = \count($str_parts); + + if ($format === 1) { + $number_of_words = []; + for ($i = 1; $i < $len; $i += 2) { + $number_of_words[] = $str_parts[$i]; + } + + return $number_of_words; + } + + if ($format === 2) { + $number_of_words = []; + $offset = (int) self::strlen($str_parts[0]); + for ($i = 1; $i < $len; $i += 2) { + $number_of_words[$offset] = $str_parts[$i]; + $offset += (int) self::strlen($str_parts[$i]) + (int) self::strlen($str_parts[$i + 1]); + } + + return $number_of_words; + } + + $number_of_words = (int) (($len - 1) / 2); + + /* @phpstan-ignore-next-line | it should be 0|positive-int, maybe nested "phpstan-return" is not working? */ + return $number_of_words; + } + + /** + * Case-insensitive string comparison. + * + * INFO: Case-insensitive version of UTF8::strcmp() + * + * EXAMPLE: UTF8::strcasecmp("iñtërnâtiôn\nàlizætiøn", "Iñtërnâtiôn\nàlizætiøn"); // 0 + * + * @param string $str1

The first string.

+ * @param string $str2

The second string.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return int + * < 0 if str1 is less than str2;
+ * > 0 if str1 is greater than str2,
+ * 0 if they are equal + */ + public static function strcasecmp( + string $str1, + string $str2, + string $encoding = 'UTF-8' + ): int { + return self::strcmp( + self::strtocasefold( + $str1, + true, + false, + $encoding, + null, + false + ), + self::strtocasefold( + $str2, + true, + false, + $encoding, + null, + false + ) + ); + } + + /** + * Case-sensitive string comparison. + * + * EXAMPLE: UTF8::strcmp("iñtërnâtiôn\nàlizætiøn", "iñtërnâtiôn\nàlizætiøn"); // 0 + * + * @param string $str1

The first string.

+ * @param string $str2

The second string.

+ * + * @psalm-pure + * + * @return int + * < 0 if str1 is less than str2
+ * > 0 if str1 is greater than str2
+ * 0 if they are equal + */ + public static function strcmp(string $str1, string $str2): int + { + if ($str1 === $str2) { + return 0; + } + + return \strcmp( + \Normalizer::normalize($str1, \Normalizer::NFD), + \Normalizer::normalize($str2, \Normalizer::NFD) + ); + } + + /** + * Find length of initial segment not matching mask. + * + * @param string $str + * @param string $char_list + * @param int $offset + * @param int|null $length + * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return int + * + * @phpstan-return 0|positive-int + */ + public static function strcspn( + string $str, + string $char_list, + int $offset = 0, + int $length = null, + string $encoding = 'UTF-8' + ): int { + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + if ($char_list === '') { + return (int) self::strlen($str, $encoding); + } + + if ($offset || $length !== null) { + if ($encoding === 'UTF-8') { + if ($length === null) { + $str_tmp = \mb_substr($str, $offset); + } else { + $str_tmp = \mb_substr($str, $offset, $length); + } + } else { + $str_tmp = self::substr($str, $offset, $length, $encoding); + } + + if ($str_tmp === false) { + return 0; + } + + $str = $str_tmp; + } + + if ($str === '') { + return 0; + } + + $matches = []; + if (\preg_match('/^(.*?)' . self::rxClass($char_list) . '/us', $str, $matches)) { + $return = self::strlen($matches[1], $encoding); + if ($return === false) { + return 0; + } + + return $return; + } + + return (int) self::strlen($str, $encoding); + } + + /** + * Create a UTF-8 string from code points. + * + * INFO: opposite to UTF8::codepoints() + * + * EXAMPLE: UTF8::string(array(246, 228, 252)); // 'öäü' + * + * @param int|int[]|string|string[] $intOrHex

Integer or Hexadecimal codepoints.

+ * + * @phpstan-param int[]|numeric-string[]|int|numeric-string $intOrHex + * + * @psalm-pure + * + * @return string + *

A UTF-8 encoded string.

+ */ + public static function string($intOrHex): string + { + if ($intOrHex === []) { + return ''; + } + + if (!\is_array($intOrHex)) { + $intOrHex = [$intOrHex]; + } + + $str = ''; + foreach ($intOrHex as $strPart) { + $str .= '&#' . (int) $strPart . ';'; + } + + // We cannot use html_entity_decode() here, as it will not return + // characters for many values < 160. + return mb_convert_encoding($str, 'UTF-8', 'HTML-ENTITIES'); + } + + /** + * Checks if string starts with "BOM" (Byte Order Mark Character) character. + * + * EXAMPLE: UTF8::string_has_bom("\xef\xbb\xbf foobar"); // true + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return bool + *

+ * true if the string has BOM at the start,
+ * false otherwise + *

+ */ + public static function string_has_bom(string $str): bool + { + foreach (self::$BOM as $bom_string => &$bom_byte_length) { + if (\strncmp($str, $bom_string, $bom_byte_length) === 0) { + return true; + } + } + + return false; + } + + /** + * Strip HTML and PHP tags from a string + clean invalid UTF-8. + * + * EXAMPLE: UTF8::strip_tags("κόσμε\xa0\xa1"); // 'κόσμε' + * + * @see http://php.net/manual/en/function.strip-tags.php + * + * @param string $str

+ * The input string. + *

+ * @param string|null $allowable_tags [optional]

+ * You can use the optional second parameter to specify tags which should + * not be stripped. + *

+ *

+ * HTML comments and PHP tags are also stripped. This is hardcoded and + * can not be changed with allowable_tags. + *

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * + * @psalm-pure + * + * @return string + *

The stripped string.

+ */ + public static function strip_tags( + string $str, + string $allowable_tags = null, + bool $clean_utf8 = false + ): string { + if ($str === '') { + return ''; + } + + if ($clean_utf8) { + $str = self::clean($str); + } + + if ($allowable_tags === null) { + return \strip_tags($str); + } + + return \strip_tags($str, $allowable_tags); + } + + /** + * Strip all whitespace characters. This includes tabs and newline + * characters, as well as multibyte whitespace such as the thin space + * and ideographic space. + * + * EXAMPLE: UTF8::strip_whitespace(' Ο συγγραφέας '); // 'Οσυγγραφέας' + * + * @param string $str + * + * @psalm-pure + * + * @return string + */ + public static function strip_whitespace(string $str): string + { + if ($str === '') { + return ''; + } + + return (string) \preg_replace('/[[:space:]]+/u', '', $str); + } + + /** + * Find the position of the first occurrence of a substring in a string, case-insensitive. + * + * INFO: use UTF8::stripos_in_byte() for the byte-length + * + * EXAMPLE: UTF8::stripos('aσσb', 'ΣΣ'); // 1 (σσ == ΣΣ) + * + * @see http://php.net/manual/en/function.mb-stripos.php + * + * @param string $haystack

The string from which to get the position of the first occurrence of needle.

+ * @param string $needle

The string to find in haystack.

+ * @param int $offset [optional]

The position in haystack to start searching.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * + * @psalm-pure + * + * @return false|int + * Return the (int) numeric position of the first occurrence of needle in the + * haystack string,
or false if needle is not found + * + * @phpstan-return false|0|positive-int + */ + public static function stripos( + string $haystack, + string $needle, + int $offset = 0, + string $encoding = 'UTF-8', + bool $clean_utf8 = false + ) { + if ($haystack === '') { + if (\PHP_VERSION_ID >= 80000 && $needle === '') { + return 0; + } + + return false; + } + + if ($needle === '' && \PHP_VERSION_ID < 80000) { + return false; + } + + if ($clean_utf8) { + // "mb_strpos()" and "iconv_strpos()" returns wrong position, + // if invalid characters are found in $haystack before $needle + $haystack = self::clean($haystack); + $needle = self::clean($needle); + } + + if (self::$SUPPORT['mbstring'] === true) { + if ($encoding === 'UTF-8') { + return \mb_stripos($haystack, $needle, $offset); + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + return \mb_stripos($haystack, $needle, $offset, $encoding); + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + if ( + $encoding === 'UTF-8' // INFO: "grapheme_stripos()" can't handle other encodings + && + $offset >= 0 // grapheme_stripos() can't handle negative offset + && + self::$SUPPORT['intl'] === true + ) { + $return_tmp = \grapheme_stripos($haystack, $needle, $offset); + if ($return_tmp !== false) { + return $return_tmp; + } + } + + // + // fallback for ascii only + // + + if (ASCII::is_ascii($haystack . $needle)) { + return \stripos($haystack, $needle, $offset); + } + + // + // fallback via vanilla php + // + + $haystack = self::strtocasefold($haystack, true, false, $encoding, null, false); + $needle = self::strtocasefold($needle, true, false, $encoding, null, false); + + return self::strpos($haystack, $needle, $offset, $encoding); + } + + /** + * Returns all of haystack starting from and including the first occurrence of needle to the end. + * + * EXAMPLE: + * $str = 'iñtërnâtiônàlizætiøn'; + * $search = 'NÂT'; + * + * UTF8::stristr($str, $search)); // 'nâtiônàlizætiøn' + * UTF8::stristr($str, $search, true)); // 'iñtër' + * + * + * @param string $haystack

The input string. Must be valid UTF-8.

+ * @param string $needle

The string to look for. Must be valid UTF-8.

+ * @param bool $before_needle [optional]

+ * If TRUE, it returns the part of the + * haystack before the first occurrence of the needle (excluding the needle). + *

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * + * @psalm-pure + * + * @return false|string + *

A sub-string,
or false if needle is not found.

+ */ + public static function stristr( + string $haystack, + string $needle, + bool $before_needle = false, + string $encoding = 'UTF-8', + bool $clean_utf8 = false + ) { + if ($haystack === '') { + if (\PHP_VERSION_ID >= 80000 && $needle === '') { + return ''; + } + + return false; + } + + if ($clean_utf8) { + // "mb_strpos()" and "iconv_strpos()" returns wrong position, + // if invalid characters are found in $haystack before $needle + $needle = self::clean($needle); + $haystack = self::clean($haystack); + } + + if ($needle === '') { + if (\PHP_VERSION_ID >= 80000) { + return $haystack; + } + + return false; + } + + if (self::$SUPPORT['mbstring'] === true) { + if ($encoding === 'UTF-8') { + return \mb_stristr($haystack, $needle, $before_needle); + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + return \mb_stristr($haystack, $needle, $before_needle, $encoding); + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + if ( + $encoding !== 'UTF-8' + && + self::$SUPPORT['mbstring'] === false + ) { + /** + * @psalm-suppress ImpureFunctionCall - this is only a warning + */ + \trigger_error('UTF8::stristr() without mbstring cannot handle "' . $encoding . '" encoding', \E_USER_WARNING); + } + + if ( + $encoding === 'UTF-8' // INFO: "grapheme_stristr()" can't handle other encodings + && + self::$SUPPORT['intl'] === true + ) { + $return_tmp = \grapheme_stristr($haystack, $needle, $before_needle); + if ($return_tmp !== false) { + return $return_tmp; + } + } + + if (ASCII::is_ascii($needle . $haystack)) { + return \stristr($haystack, $needle, $before_needle); + } + + \preg_match('/^(.*?)' . \preg_quote($needle, '/') . '/usi', $haystack, $match); + + if (!isset($match[1])) { + return false; + } + + if ($before_needle) { + return $match[1]; + } + + return self::substr($haystack, (int) self::strlen($match[1], $encoding), null, $encoding); + } + + /** + * Get the string length, not the byte-length! + * + * INFO: use UTF8::strwidth() for the char-length + * + * EXAMPLE: UTF8::strlen("Iñtërnâtiôn\xE9àlizætiøn")); // 20 + * + * @see http://php.net/manual/en/function.mb-strlen.php + * + * @param string $str

The string being checked for length.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * + * @psalm-pure + * + * @return false|int + *

+ * The number (int) of characters in the string $str having character encoding + * $encoding. + * (One multi-byte character counted as +1). + *
+ * Can return false, if e.g. mbstring is not installed and we process invalid + * chars. + *

+ * + * @phpstan-return false|0|positive-int + */ + public static function strlen( + string $str, + string $encoding = 'UTF-8', + bool $clean_utf8 = false + ) { + if ($str === '') { + return 0; + } + + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + if ($clean_utf8) { + // "mb_strlen" and "\iconv_strlen" returns wrong length, + // if invalid characters are found in $str + $str = self::clean($str); + } + + // + // fallback via mbstring + // + + if (self::$SUPPORT['mbstring'] === true) { + if ($encoding === 'UTF-8') { + /** @noinspection PhpUsageOfSilenceOperatorInspection - ignore warnings, it's working anyway */ + return @\mb_strlen($str); + } + + /** @noinspection PhpUsageOfSilenceOperatorInspection - ignore warnings, it's working anyway */ + return @\mb_strlen($str, $encoding); + } + + // + // fallback for binary || ascii only + // + + if ( + $encoding === 'CP850' + || + $encoding === 'ASCII' + ) { + return \strlen($str); + } + + if ( + $encoding !== 'UTF-8' + && + self::$SUPPORT['mbstring'] === false + && + self::$SUPPORT['iconv'] === false + ) { + /** + * @psalm-suppress ImpureFunctionCall - this is only a warning + */ + \trigger_error('UTF8::strlen() without mbstring / iconv cannot handle "' . $encoding . '" encoding', \E_USER_WARNING); + } + + // + // fallback via iconv + // + + if (self::$SUPPORT['iconv'] === true) { + $return_tmp = \iconv_strlen($str, $encoding); + if ($return_tmp !== false) { + return $return_tmp; + } + } + + // + // fallback via intl + // + + if ( + $encoding === 'UTF-8' // INFO: "grapheme_strlen()" can't handle other encodings + && + self::$SUPPORT['intl'] === true + ) { + $return_tmp = \grapheme_strlen($str); + /* @phpstan-ignore-next-line | "grapheme_strlen" will maybe return "null" for empty-strings and "false" on error */ + if ($return_tmp !== false && $return_tmp !== null) { + return $return_tmp; + } + } + + // + // fallback for ascii only + // + + if (ASCII::is_ascii($str)) { + return \strlen($str); + } + + // + // fallback via vanilla php + // + + \preg_match_all('/./us', $str, $parts); + + $return_tmp = \count($parts[0]); + if ($return_tmp === 0) { + return false; + } + + return $return_tmp; + } + + /** + * Get string length in byte. + * + * @param string $str + * + * @psalm-pure + * + * @return int + * + * @phpstan-return 0|positive-int + */ + public static function strlen_in_byte(string $str): int + { + if ($str === '') { + return 0; + } + + if (self::$SUPPORT['mbstring_func_overload'] === true) { + // "mb_" is available if overload is used, so use it ... + return \mb_strlen($str, 'CP850'); // 8-BIT + } + + return \strlen($str); + } + + /** + * Case-insensitive string comparisons using a "natural order" algorithm. + * + * INFO: natural order version of UTF8::strcasecmp() + * + * EXAMPLES: + * UTF8::strnatcasecmp('2', '10Hello WORLD 中文空白!'); // -1 + * UTF8::strcasecmp('2Hello world 中文空白!', '10Hello WORLD 中文空白!'); // 1 + * + * UTF8::strnatcasecmp('10Hello world 中文空白!', '2Hello WORLD 中文空白!'); // 1 + * UTF8::strcasecmp('10Hello world 中文空白!', '2Hello WORLD 中文空白!'); // -1 + * + * + * @param string $str1

The first string.

+ * @param string $str2

The second string.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return int + * < 0 if str1 is less than str2
+ * > 0 if str1 is greater than str2
+ * 0 if they are equal + */ + public static function strnatcasecmp(string $str1, string $str2, string $encoding = 'UTF-8'): int + { + return self::strnatcmp( + self::strtocasefold($str1, true, false, $encoding, null, false), + self::strtocasefold($str2, true, false, $encoding, null, false) + ); + } + + /** + * String comparisons using a "natural order" algorithm + * + * INFO: natural order version of UTF8::strcmp() + * + * EXAMPLES: + * UTF8::strnatcmp('2Hello world 中文空白!', '10Hello WORLD 中文空白!'); // -1 + * UTF8::strcmp('2Hello world 中文空白!', '10Hello WORLD 中文空白!'); // 1 + * + * UTF8::strnatcmp('10Hello world 中文空白!', '2Hello WORLD 中文空白!'); // 1 + * UTF8::strcmp('10Hello world 中文空白!', '2Hello WORLD 中文空白!'); // -1 + * + * + * @see http://php.net/manual/en/function.strnatcmp.php + * + * @param string $str1

The first string.

+ * @param string $str2

The second string.

+ * + * @psalm-pure + * + * @return int + * < 0 if str1 is less than str2;
+ * > 0 if str1 is greater than str2;
+ * 0 if they are equal + */ + public static function strnatcmp(string $str1, string $str2): int + { + if ($str1 === $str2) { + return 0; + } + + return \strnatcmp( + (string) self::strtonatfold($str1), + (string) self::strtonatfold($str2) + ); + } + + /** + * Case-insensitive string comparison of the first n characters. + * + * EXAMPLE: + * UTF8::strcasecmp("iñtërnâtiôn\nàlizætiøn321", "iñtërnâtiôn\nàlizætiøn123", 5); // 0 + * + * + * @see http://php.net/manual/en/function.strncasecmp.php + * + * @param string $str1

The first string.

+ * @param string $str2

The second string.

+ * @param int $len

The length of strings to be used in the comparison.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return int + * < 0 if str1 is less than str2;
+ * > 0 if str1 is greater than str2;
+ * 0 if they are equal + */ + public static function strncasecmp( + string $str1, + string $str2, + int $len, + string $encoding = 'UTF-8' + ): int { + return self::strncmp( + self::strtocasefold($str1, true, false, $encoding, null, false), + self::strtocasefold($str2, true, false, $encoding, null, false), + $len + ); + } + + /** + * String comparison of the first n characters. + * + * EXAMPLE: + * UTF8::strncmp("Iñtërnâtiôn\nàlizætiøn321", "Iñtërnâtiôn\nàlizætiøn123", 5); // 0 + * + * + * @see http://php.net/manual/en/function.strncmp.php + * + * @param string $str1

The first string.

+ * @param string $str2

The second string.

+ * @param int $len

Number of characters to use in the comparison.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return int + * < 0 if str1 is less than str2;
+ * > 0 if str1 is greater than str2;
+ * 0 if they are equal + */ + public static function strncmp( + string $str1, + string $str2, + int $len, + string $encoding = 'UTF-8' + ): int { + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + if ($encoding === 'UTF-8') { + $str1 = (string) \mb_substr($str1, 0, $len); + $str2 = (string) \mb_substr($str2, 0, $len); + } else { + $str1 = (string) self::substr($str1, 0, $len, $encoding); + $str2 = (string) self::substr($str2, 0, $len, $encoding); + } + + return self::strcmp($str1, $str2); + } + + /** + * Search a string for any of a set of characters. + * + * EXAMPLE: UTF8::strpbrk('-中文空白-', '白'); // '白-' + * + * @see http://php.net/manual/en/function.strpbrk.php + * + * @param string $haystack

The string where char_list is looked for.

+ * @param string $char_list

This parameter is case-sensitive.

+ * + * @psalm-pure + * + * @return false|string + *

The string starting from the character found, or false if it is not found.

+ */ + public static function strpbrk(string $haystack, string $char_list) + { + if ($haystack === '' || $char_list === '') { + return false; + } + + if (\preg_match('/' . self::rxClass($char_list) . '/us', $haystack, $m)) { + return \substr($haystack, (int) \strpos($haystack, $m[0])); + } + + return false; + } + + /** + * Find the position of the first occurrence of a substring in a string. + * + * INFO: use UTF8::strpos_in_byte() for the byte-length + * + * EXAMPLE: UTF8::strpos('ABC-ÖÄÜ-中文空白-中文空白', '中'); // 8 + * + * @see http://php.net/manual/en/function.mb-strpos.php + * + * @param string $haystack

The string from which to get the position of the first occurrence of needle.

+ * @param int|string $needle

The string to find in haystack.
Or a code point as int.

+ * @param int $offset [optional]

The search offset. If it is not specified, 0 is used.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * + * @psalm-pure + * + * @return false|int + * The (int) numeric position of the first occurrence of needle in the haystack + * string.
If needle is not found it returns false. + * + * @phpstan-return false|0|positive-int + */ + public static function strpos( + string $haystack, + $needle, + int $offset = 0, + string $encoding = 'UTF-8', + bool $clean_utf8 = false + ) { + if ($haystack === '') { + if (\PHP_VERSION_ID >= 80000) { + if ($needle === '') { + return 0; + } + } else { + return false; + } + } + + // iconv and mbstring do not support integer $needle + if ((int) $needle === $needle) { + $needle = (string) self::chr($needle); + } + $needle = (string) $needle; + + if ($haystack === '') { + if (\PHP_VERSION_ID >= 80000 && $needle === '') { + return 0; + } + + return false; + } + + if ($needle === '' && \PHP_VERSION_ID < 80000) { + return false; + } + + if ($clean_utf8) { + // "mb_strpos()" and "iconv_strpos()" returns wrong position, + // if invalid characters are found in $haystack before $needle + $needle = self::clean($needle); + $haystack = self::clean($haystack); + } + + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + // + // fallback via mbstring + // + + if (self::$SUPPORT['mbstring'] === true) { + if ($encoding === 'UTF-8') { + /** @noinspection PhpUsageOfSilenceOperatorInspection - Offset not contained in string */ + return @\mb_strpos($haystack, $needle, $offset); + } + + /** @noinspection PhpUsageOfSilenceOperatorInspection - Offset not contained in string */ + return @\mb_strpos($haystack, $needle, $offset, $encoding); + } + + // + // fallback for binary || ascii only + // + if ( + $encoding === 'CP850' + || + $encoding === 'ASCII' + ) { + return \strpos($haystack, $needle, $offset); + } + + if ( + $encoding !== 'UTF-8' + && + self::$SUPPORT['iconv'] === false + && + self::$SUPPORT['mbstring'] === false + ) { + /** + * @psalm-suppress ImpureFunctionCall - this is only a warning + */ + \trigger_error('UTF8::strpos() without mbstring / iconv cannot handle "' . $encoding . '" encoding', \E_USER_WARNING); + } + + // + // fallback via intl + // + + if ( + $encoding === 'UTF-8' // INFO: "grapheme_strpos()" can't handle other encodings + && + $offset >= 0 // grapheme_strpos() can't handle negative offset + && + self::$SUPPORT['intl'] === true + ) { + $return_tmp = \grapheme_strpos($haystack, $needle, $offset); + if ($return_tmp !== false) { + return $return_tmp; + } + } + + // + // fallback via iconv + // + + if ( + $offset >= 0 // iconv_strpos() can't handle negative offset + && + self::$SUPPORT['iconv'] === true + ) { + // ignore invalid negative offset to keep compatibility + // with php < 5.5.35, < 5.6.21, < 7.0.6 + $return_tmp = \iconv_strpos($haystack, $needle, $offset > 0 ? $offset : 0, $encoding); + if ($return_tmp !== false) { + return $return_tmp; + } + } + + // + // fallback for ascii only + // + + if (ASCII::is_ascii($haystack . $needle)) { + /** @noinspection PhpUsageOfSilenceOperatorInspection - Offset not contained in string */ + return @\strpos($haystack, $needle, $offset); + } + + // + // fallback via vanilla php + // + + $haystack_tmp = self::substr($haystack, $offset, null, $encoding); + if ($haystack_tmp === false) { + $haystack_tmp = ''; + } + $haystack = (string) $haystack_tmp; + + if ($offset < 0) { + $offset = 0; + } + + $pos = \strpos($haystack, $needle); + if ($pos === false) { + return false; + } + + if ($pos) { + return $offset + (int) self::strlen(\substr($haystack, 0, $pos), $encoding); + } + + return $offset + 0; + } + + /** + * Find the position of the first occurrence of a substring in a string. + * + * @param string $haystack

+ * The string being checked. + *

+ * @param string $needle

+ * The position counted from the beginning of haystack. + *

+ * @param int $offset [optional]

+ * The search offset. If it is not specified, 0 is used. + *

+ * + * @psalm-pure + * + * @return false|int + *

The numeric position of the first occurrence of needle in the + * haystack string. If needle is not found, it returns false.

+ * + * @phpstan-return false|0|positive-int + */ + public static function strpos_in_byte(string $haystack, string $needle, int $offset = 0) + { + if ($haystack === '' || $needle === '') { + return false; + } + + if (self::$SUPPORT['mbstring_func_overload'] === true) { + // "mb_" is available if overload is used, so use it ... + return \mb_strpos($haystack, $needle, $offset, 'CP850'); // 8-BIT + } + + return \strpos($haystack, $needle, $offset); + } + + /** + * Find the position of the first occurrence of a substring in a string, case-insensitive. + * + * @param string $haystack

+ * The string being checked. + *

+ * @param string $needle

+ * The position counted from the beginning of haystack. + *

+ * @param int $offset [optional]

+ * The search offset. If it is not specified, 0 is used. + *

+ * + * @psalm-pure + * + * @return false|int + *

The numeric position of the first occurrence of needle in the + * haystack string. If needle is not found, it returns false.

+ * + * @phpstan-return false|0|positive-int + */ + public static function stripos_in_byte(string $haystack, string $needle, int $offset = 0) + { + if ($haystack === '' || $needle === '') { + return false; + } + + if (self::$SUPPORT['mbstring_func_overload'] === true) { + // "mb_" is available if overload is used, so use it ... + return \mb_stripos($haystack, $needle, $offset, 'CP850'); // 8-BIT + } + + return \stripos($haystack, $needle, $offset); + } + + /** + * Find the last occurrence of a character in a string within another. + * + * EXAMPLE: UTF8::strrchr('κόσμεκόσμε-äöü', 'κόσμε'); // 'κόσμε-äöü' + * + * @see http://php.net/manual/en/function.mb-strrchr.php + * + * @param string $haystack

The string from which to get the last occurrence of needle.

+ * @param string $needle

The string to find in haystack

+ * @param bool $before_needle [optional]

+ * Determines which portion of haystack + * this function returns. + * If set to true, it returns all of haystack + * from the beginning to the last occurrence of needle. + * If set to false, it returns all of haystack + * from the last occurrence of needle to the end, + *

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * + * @psalm-pure + * + * @return false|string + *

The portion of haystack or false if needle is not found.

+ */ + public static function strrchr( + string $haystack, + string $needle, + bool $before_needle = false, + string $encoding = 'UTF-8', + bool $clean_utf8 = false + ) { + if ($haystack === '' || $needle === '') { + return false; + } + + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + if ($clean_utf8) { + // "mb_strpos()" and "iconv_strpos()" returns wrong position, + // if invalid characters are found in $haystack before $needle + $needle = self::clean($needle); + $haystack = self::clean($haystack); + } + + // + // fallback via mbstring + // + + if (self::$SUPPORT['mbstring'] === true) { + if ($encoding === 'UTF-8') { + return \mb_strrchr($haystack, $needle, $before_needle); + } + + return \mb_strrchr($haystack, $needle, $before_needle, $encoding); + } + + // + // fallback for binary || ascii only + // + + if ( + !$before_needle + && + ( + $encoding === 'CP850' + || + $encoding === 'ASCII' + ) + ) { + return \strrchr($haystack, $needle); + } + + if ( + $encoding !== 'UTF-8' + && + self::$SUPPORT['mbstring'] === false + ) { + /** + * @psalm-suppress ImpureFunctionCall - this is only a warning + */ + \trigger_error('UTF8::strrchr() without mbstring cannot handle "' . $encoding . '" encoding', \E_USER_WARNING); + } + + // + // fallback via iconv + // + + if (self::$SUPPORT['iconv'] === true) { + $needle_tmp = self::substr($needle, 0, 1, $encoding); + if ($needle_tmp === false) { + return false; + } + $needle = $needle_tmp; + + $pos = \iconv_strrpos($haystack, $needle, $encoding); + if ($pos === false) { + return false; + } + + if ($before_needle) { + return self::substr($haystack, 0, $pos, $encoding); + } + + return self::substr($haystack, $pos, null, $encoding); + } + + // + // fallback via vanilla php + // + + $needle_tmp = self::substr($needle, 0, 1, $encoding); + if ($needle_tmp === false) { + return false; + } + $needle = $needle_tmp; + + $pos = self::strrpos($haystack, $needle, 0, $encoding); + if ($pos === false) { + return false; + } + + if ($before_needle) { + return self::substr($haystack, 0, $pos, $encoding); + } + + return self::substr($haystack, $pos, null, $encoding); + } + + /** + * Reverses characters order in the string. + * + * EXAMPLE: UTF8::strrev('κ-öäü'); // 'üäö-κ' + * + * @param string $str

The input string.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + *

The string with characters in the reverse sequence.

+ */ + public static function strrev(string $str, string $encoding = 'UTF-8'): string + { + if ($str === '') { + return ''; + } + + // init + $reversed = ''; + + $str = self::emoji_encode($str, true); + + if ($encoding === 'UTF-8') { + if (self::$SUPPORT['intl'] === true) { + // try "grapheme" first: https://stackoverflow.com/questions/17496493/strrev-dosent-support-utf-8 + $i = (int) \grapheme_strlen($str); + while ($i--) { + $reversed_tmp = \grapheme_substr($str, $i, 1); + if ($reversed_tmp !== false) { + $reversed .= $reversed_tmp; + } + } + } else { + $i = (int) \mb_strlen($str); + while ($i--) { + $reversed_tmp = \mb_substr($str, $i, 1); + if ($reversed_tmp !== false) { /* @phpstan-ignore-line | old polyfill will return false, or? */ + $reversed .= $reversed_tmp; + } + } + } + } else { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + $i = (int) self::strlen($str, $encoding); + while ($i--) { + $reversed_tmp = self::substr($str, $i, 1, $encoding); + if ($reversed_tmp !== false) { + $reversed .= $reversed_tmp; + } + } + } + + return self::emoji_decode($reversed, true); + } + + /** + * Find the last occurrence of a character in a string within another, case-insensitive. + * + * EXAMPLE: UTF8::strrichr('Aκόσμεκόσμε-äöü', 'aκόσμε'); // 'Aκόσμεκόσμε-äöü' + * + * @see http://php.net/manual/en/function.mb-strrichr.php + * + * @param string $haystack

The string from which to get the last occurrence of needle.

+ * @param string $needle

The string to find in haystack.

+ * @param bool $before_needle [optional]

+ * Determines which portion of haystack + * this function returns. + * If set to true, it returns all of haystack + * from the beginning to the last occurrence of needle. + * If set to false, it returns all of haystack + * from the last occurrence of needle to the end, + *

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * + * @psalm-pure + * + * @return false|string + *

The portion of haystack or
false if needle is not found.

+ */ + public static function strrichr( + string $haystack, + string $needle, + bool $before_needle = false, + string $encoding = 'UTF-8', + bool $clean_utf8 = false + ) { + if ($haystack === '' || $needle === '') { + return false; + } + + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + if ($clean_utf8) { + // "mb_strpos()" and "iconv_strpos()" returns wrong position, + // if invalid characters are found in $haystack before $needle + $needle = self::clean($needle); + $haystack = self::clean($haystack); + } + + // + // fallback via mbstring + // + + if (self::$SUPPORT['mbstring'] === true) { + if ($encoding === 'UTF-8') { + return \mb_strrichr($haystack, $needle, $before_needle); + } + + return \mb_strrichr($haystack, $needle, $before_needle, $encoding); + } + + // + // fallback via vanilla php + // + + $needle_tmp = self::substr($needle, 0, 1, $encoding); + if ($needle_tmp === false) { + return false; + } + $needle = $needle_tmp; + + $pos = self::strripos($haystack, $needle, 0, $encoding); + if ($pos === false) { + return false; + } + + if ($before_needle) { + return self::substr($haystack, 0, $pos, $encoding); + } + + return self::substr($haystack, $pos, null, $encoding); + } + + /** + * Find the position of the last occurrence of a substring in a string, case-insensitive. + * + * EXAMPLE: UTF8::strripos('ABC-ÖÄÜ-中文空白-中文空白', '中'); // 13 + * + * @param string $haystack

The string to look in.

+ * @param int|string $needle

The string to look for.

+ * @param int $offset [optional]

Number of characters to ignore in the beginning or end.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * + * @psalm-pure + * + * @return false|int + *

The (int) numeric position of the last occurrence of needle in the haystack + * string.
If needle is not found, it returns false.

+ */ + public static function strripos( + string $haystack, + $needle, + int $offset = 0, + string $encoding = 'UTF-8', + bool $clean_utf8 = false + ) { + if ($haystack === '') { + if (\PHP_VERSION_ID >= 80000) { + if ($needle === '') { + return 0; + } + } else { + return false; + } + } + + // iconv and mbstring do not support integer $needle + if ((int) $needle === $needle && $needle >= 0) { + $needle = (string) self::chr($needle); + } + $needle = (string) $needle; + + if ($haystack === '') { + if (\PHP_VERSION_ID >= 80000 && $needle === '') { + return 0; + } + + return false; + } + + if ($needle === '' && \PHP_VERSION_ID < 80000) { + return false; + } + + if ($clean_utf8) { + // mb_strripos() && iconv_strripos() is not tolerant to invalid characters + $needle = self::clean($needle); + $haystack = self::clean($haystack); + } + + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + // + // fallback via mbstrig + // + + if (self::$SUPPORT['mbstring'] === true) { + if ($encoding === 'UTF-8') { + return \mb_strripos($haystack, $needle, $offset); + } + + return \mb_strripos($haystack, $needle, $offset, $encoding); + } + + // + // fallback for binary || ascii only + // + + if ( + $encoding === 'CP850' + || + $encoding === 'ASCII' + ) { + return \strripos($haystack, $needle, $offset); + } + + if ( + $encoding !== 'UTF-8' + && + self::$SUPPORT['mbstring'] === false + ) { + /** + * @psalm-suppress ImpureFunctionCall - this is only a warning + */ + \trigger_error('UTF8::strripos() without mbstring cannot handle "' . $encoding . '" encoding', \E_USER_WARNING); + } + + // + // fallback via intl + // + + if ( + $encoding === 'UTF-8' // INFO: "grapheme_strripos()" can't handle other encodings + && + $offset >= 0 // grapheme_strripos() can't handle negative offset + && + self::$SUPPORT['intl'] === true + ) { + $return_tmp = \grapheme_strripos($haystack, $needle, $offset); + if ($return_tmp !== false) { + return $return_tmp; + } + } + + // + // fallback for ascii only + // + + if (ASCII::is_ascii($haystack . $needle)) { + return \strripos($haystack, $needle, $offset); + } + + // + // fallback via vanilla php + // + + $haystack = self::strtocasefold($haystack, true, false, $encoding); + $needle = self::strtocasefold($needle, true, false, $encoding); + + return self::strrpos($haystack, $needle, $offset, $encoding, $clean_utf8); + } + + /** + * Finds position of last occurrence of a string within another, case-insensitive. + * + * @param string $haystack

+ * The string from which to get the position of the last occurrence + * of needle. + *

+ * @param string $needle

+ * The string to find in haystack. + *

+ * @param int $offset [optional]

+ * The position in haystack + * to start searching. + *

+ * + * @psalm-pure + * + * @return false|int + *

eturn the numeric position of the last occurrence of needle in the + * haystack string, or false if needle is not found.

+ */ + public static function strripos_in_byte(string $haystack, string $needle, int $offset = 0) + { + if ($haystack === '' || $needle === '') { + return false; + } + + if (self::$SUPPORT['mbstring_func_overload'] === true) { + // "mb_" is available if overload is used, so use it ... + return \mb_strripos($haystack, $needle, $offset, 'CP850'); // 8-BIT + } + + return \strripos($haystack, $needle, $offset); + } + + /** + * Find the position of the last occurrence of a substring in a string. + * + * EXAMPLE: UTF8::strrpos('ABC-ÖÄÜ-中文空白-中文空白', '中'); // 13 + * + * @see http://php.net/manual/en/function.mb-strrpos.php + * + * @param string $haystack

The string being checked, for the last occurrence of needle

+ * @param int|string $needle

The string to find in haystack.
Or a code point as int.

+ * @param int $offset [optional]

May be specified to begin searching an arbitrary number of characters + * into the string. Negative values will stop searching at an arbitrary point prior to + * the end of the string. + *

+ * @param string $encoding [optional]

Set the charset.

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * + * @psalm-pure + * + * @return false|int + *

The (int) numeric position of the last occurrence of needle in the haystack + * string.
If needle is not found, it returns false.

+ */ + public static function strrpos( + string $haystack, + $needle, + int $offset = 0, + string $encoding = 'UTF-8', + bool $clean_utf8 = false + ) { + if ($haystack === '') { + if (\PHP_VERSION_ID >= 80000) { + if ($needle === '') { + return 0; + } + } else { + return false; + } + } + + // iconv and mbstring do not support integer $needle + if ((int) $needle === $needle && $needle >= 0) { + $needle = (string) self::chr($needle); + } + $needle = (string) $needle; + + if ($haystack === '') { + if (\PHP_VERSION_ID >= 80000 && $needle === '') { + return 0; + } + + return false; + } + + if ($needle === '' && \PHP_VERSION_ID < 80000) { + return false; + } + + if ($clean_utf8) { + // mb_strrpos && iconv_strrpos is not tolerant to invalid characters + $needle = self::clean($needle); + $haystack = self::clean($haystack); + } + + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + // + // fallback via mbstring + // + + if (self::$SUPPORT['mbstring'] === true) { + if ($encoding === 'UTF-8') { + return \mb_strrpos($haystack, $needle, $offset); + } + + return \mb_strrpos($haystack, $needle, $offset, $encoding); + } + + // + // fallback for binary || ascii only + // + + if ( + $encoding === 'CP850' + || + $encoding === 'ASCII' + ) { + return \strrpos($haystack, $needle, $offset); + } + + if ( + $encoding !== 'UTF-8' + && + self::$SUPPORT['mbstring'] === false + ) { + /** + * @psalm-suppress ImpureFunctionCall - this is only a warning + */ + \trigger_error('UTF8::strrpos() without mbstring cannot handle "' . $encoding . '" encoding', \E_USER_WARNING); + } + + // + // fallback via intl + // + + if ( + $offset >= 0 // grapheme_strrpos() can't handle negative offset + && + $encoding === 'UTF-8' // INFO: "grapheme_strrpos()" can't handle other encodings + && + self::$SUPPORT['intl'] === true + ) { + $return_tmp = \grapheme_strrpos($haystack, $needle, $offset); + if ($return_tmp !== false) { + return $return_tmp; + } + } + + // + // fallback for ascii only + // + + if (ASCII::is_ascii($haystack . $needle)) { + return \strrpos($haystack, $needle, $offset); + } + + // + // fallback via vanilla php + // + + $haystack_tmp = null; + if ($offset > 0) { + $haystack_tmp = self::substr($haystack, $offset); + } elseif ($offset < 0) { + $haystack_tmp = self::substr($haystack, 0, $offset); + $offset = 0; + } + + if ($haystack_tmp !== null) { + if ($haystack_tmp === false) { + $haystack_tmp = ''; + } + $haystack = (string) $haystack_tmp; + } + + $pos = \strrpos($haystack, $needle); + if ($pos === false) { + return false; + } + + /** @var false|string $str_tmp - needed for PhpStan (stubs error) */ + $str_tmp = \substr($haystack, 0, $pos); + if ($str_tmp === false) { + return false; + } + + return $offset + (int) self::strlen($str_tmp); + } + + /** + * Find the position of the last occurrence of a substring in a string. + * + * @param string $haystack

+ * The string being checked, for the last occurrence + * of needle. + *

+ * @param string $needle

+ * The string to find in haystack. + *

+ * @param int $offset [optional]

May be specified to begin searching an arbitrary number of characters into + * the string. Negative values will stop searching at an arbitrary point + * prior to the end of the string. + *

+ * + * @psalm-pure + * + * @return false|int + *

The numeric position of the last occurrence of needle in the + * haystack string. If needle is not found, it returns false.

+ */ + public static function strrpos_in_byte(string $haystack, string $needle, int $offset = 0) + { + if ($haystack === '' || $needle === '') { + return false; + } + + if (self::$SUPPORT['mbstring_func_overload'] === true) { + // "mb_" is available if overload is used, so use it ... + return \mb_strrpos($haystack, $needle, $offset, 'CP850'); // 8-BIT + } + + return \strrpos($haystack, $needle, $offset); + } + + /** + * Finds the length of the initial segment of a string consisting entirely of characters contained within a given + * mask. + * + * EXAMPLE: UTF8::strspn('iñtërnâtiônàlizætiøn', 'itñ'); // '3' + * + * @param string $str

The input string.

+ * @param string $mask

The mask of chars

+ * @param int $offset [optional] + * @param int|null $length [optional] + * @param string $encoding [optional]

Set the charset.

+ * + * @psalm-pure + * + * @return false|int + */ + public static function strspn( + string $str, + string $mask, + int $offset = 0, + int $length = null, + string $encoding = 'UTF-8' + ) { + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + if ($offset || $length !== null) { + if ($encoding === 'UTF-8') { + if ($length === null) { + $str = (string) \mb_substr($str, $offset); + } else { + $str = (string) \mb_substr($str, $offset, $length); + } + } else { + $str = (string) self::substr($str, $offset, $length, $encoding); + } + } + + if ($str === '' || $mask === '') { + return 0; + } + + $matches = []; + + return \preg_match('/^' . self::rxClass($mask) . '+/u', $str, $matches) ? (int) self::strlen($matches[0], $encoding) : 0; + } + + /** + * Returns part of haystack string from the first occurrence of needle to the end of haystack. + * + * EXAMPLE: + * $str = 'iñtërnâtiônàlizætiøn'; + * $search = 'nât'; + * + * UTF8::strstr($str, $search)); // 'nâtiônàlizætiøn' + * UTF8::strstr($str, $search, true)); // 'iñtër' + * + * + * @param string $haystack

The input string. Must be valid UTF-8.

+ * @param string $needle

The string to look for. Must be valid UTF-8.

+ * @param bool $before_needle [optional]

+ * If TRUE, strstr() returns the part of the + * haystack before the first occurrence of the needle (excluding the needle). + *

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * + * @psalm-pure + * + * @return false|string + *

A sub-string,
or false if needle is not found.

+ */ + public static function strstr( + string $haystack, + string $needle, + bool $before_needle = false, + string $encoding = 'UTF-8', + bool $clean_utf8 = false + ) { + if ($haystack === '') { + if (\PHP_VERSION_ID >= 80000 && $needle === '') { + return ''; + } + + return false; + } + + if ($clean_utf8) { + // "mb_strpos()" and "iconv_strpos()" returns wrong position, + // if invalid characters are found in $haystack before $needle + $needle = self::clean($needle); + $haystack = self::clean($haystack); + } + + if ($needle === '') { + if (\PHP_VERSION_ID >= 80000) { + return $haystack; + } + + return false; + } + + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + // + // fallback via mbstring + // + + if (self::$SUPPORT['mbstring'] === true) { + if ($encoding === 'UTF-8') { + return \mb_strstr($haystack, $needle, $before_needle); + } + + return \mb_strstr($haystack, $needle, $before_needle, $encoding); + } + + // + // fallback for binary || ascii only + // + + if ( + $encoding === 'CP850' + || + $encoding === 'ASCII' + ) { + return \strstr($haystack, $needle, $before_needle); + } + + if ( + $encoding !== 'UTF-8' + && + self::$SUPPORT['mbstring'] === false + ) { + /** + * @psalm-suppress ImpureFunctionCall - this is only a warning + */ + \trigger_error('UTF8::strstr() without mbstring cannot handle "' . $encoding . '" encoding', \E_USER_WARNING); + } + + // + // fallback via intl + // + + if ( + $encoding === 'UTF-8' // INFO: "grapheme_strstr()" can't handle other encodings + && + self::$SUPPORT['intl'] === true + ) { + $return_tmp = \grapheme_strstr($haystack, $needle, $before_needle); + if ($return_tmp !== false) { + return $return_tmp; + } + } + + // + // fallback for ascii only + // + + if (ASCII::is_ascii($haystack . $needle)) { + return \strstr($haystack, $needle, $before_needle); + } + + // + // fallback via vanilla php + // + + \preg_match('/^(.*?)' . \preg_quote($needle, '/') . '/us', $haystack, $match); + + if (!isset($match[1])) { + return false; + } + + if ($before_needle) { + return $match[1]; + } + + return self::substr($haystack, (int) self::strlen($match[1])); + } + + /** + * Finds first occurrence of a string within another. + * + * @param string $haystack

+ * The string from which to get the first occurrence + * of needle. + *

+ * @param string $needle

+ * The string to find in haystack. + *

+ * @param bool $before_needle [optional]

+ * Determines which portion of haystack + * this function returns. + * If set to true, it returns all of haystack + * from the beginning to the first occurrence of needle. + * If set to false, it returns all of haystack + * from the first occurrence of needle to the end, + *

+ * + * @psalm-pure + * + * @return false|string + *

The portion of haystack, + * or false if needle is not found.

+ */ + public static function strstr_in_byte( + string $haystack, + string $needle, + bool $before_needle = false + ) { + if ($haystack === '' || $needle === '') { + return false; + } + + if (self::$SUPPORT['mbstring_func_overload'] === true) { + // "mb_" is available if overload is used, so use it ... + return \mb_strstr($haystack, $needle, $before_needle, 'CP850'); // 8-BIT + } + + return \strstr($haystack, $needle, $before_needle); + } + + /** + * Unicode transformation for case-less matching. + * + * EXAMPLE: UTF8::strtocasefold('ǰ◌̱'); // 'ǰ◌̱' + * + * @see http://unicode.org/reports/tr21/tr21-5.html + * + * @param string $str

The input string.

+ * @param bool $full [optional]

+ * true, replace full case folding chars (default)
+ * false, use only limited static array [UTF8::$COMMON_CASE_FOLD] + *

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * @param string $encoding [optional]

Set the charset.

+ * @param string|null $lang [optional]

Set the language for special cases: az, el, lt, tr

+ * @param bool $lower [optional]

Use lowercase string, otherwise use uppercase string. PS: uppercase + * is for some languages better ...

+ * + * @psalm-pure + * + * @return string + */ + public static function strtocasefold( + string $str, + bool $full = true, + bool $clean_utf8 = false, + string $encoding = 'UTF-8', + string $lang = null, + bool $lower = true + ): string { + if ($str === '') { + return ''; + } + + if ($clean_utf8) { + // "mb_strpos()" and "iconv_strpos()" returns wrong position, + // if invalid characters are found in $haystack before $needle + $str = self::clean($str); + } + + $str = self::fixStrCaseHelper($str, $lower, $full); + + if ($lang === null && $encoding === 'UTF-8') { + if ($lower) { + return \mb_strtolower($str); + } + + return \mb_strtoupper($str); + } + + if ($lower) { + return self::strtolower($str, $encoding, false, $lang); + } + + return self::strtoupper($str, $encoding, false, $lang); + } + + /** + * Make a string lowercase. + * + * EXAMPLE: UTF8::strtolower('DÉJÀ Σσς Iıİi'); // 'déjà σσς iıii' + * + * @see http://php.net/manual/en/function.mb-strtolower.php + * + * @param string $str

The string being lowercased.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * @param string|null $lang [optional]

Set the language for special cases: az, el, lt, + * tr

+ * @param bool $try_to_keep_the_string_length [optional]

true === try to keep the string length: e.g. ẞ + * -> ß

+ * + * @psalm-pure + * + * @return string + *

String with all alphabetic characters converted to lowercase.

+ */ + public static function strtolower( + $str, + string $encoding = 'UTF-8', + bool $clean_utf8 = false, + string $lang = null, + bool $try_to_keep_the_string_length = false + ): string { + // init + $str = (string) $str; + + if ($str === '') { + return ''; + } + + if ($clean_utf8) { + // "mb_strpos()" and "iconv_strpos()" returns wrong position, + // if invalid characters are found in $haystack before $needle + $str = self::clean($str); + } + + // hack for old php version or for the polyfill ... + if ($try_to_keep_the_string_length) { + $str = self::fixStrCaseHelper($str, true); + } + + if ($lang === null && $encoding === 'UTF-8') { + return \mb_strtolower($str); + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + if ($lang !== null) { + if (self::$SUPPORT['intl'] === true) { + if (self::$INTL_TRANSLITERATOR_LIST === null) { + self::$INTL_TRANSLITERATOR_LIST = self::getData('transliterator_list'); + } + + $language_code = $lang . '-Lower'; + if (!\in_array($language_code, self::$INTL_TRANSLITERATOR_LIST, true)) { + /** + * @psalm-suppress ImpureFunctionCall - this is only a warning + */ + \trigger_error('UTF8::strtolower() cannot handle special language: ' . $lang . ' | supported: ' . \print_r(self::$INTL_TRANSLITERATOR_LIST, true), \E_USER_WARNING); + + $language_code = 'Any-Lower'; + } + + return (string) \transliterator_transliterate($language_code, $str); + } + + /** + * @psalm-suppress ImpureFunctionCall - this is only a warning + */ + \trigger_error('UTF8::strtolower() without intl cannot handle the "lang" parameter: ' . $lang, \E_USER_WARNING); + } + + // always fallback via symfony polyfill + return \mb_strtolower($str, $encoding); + } + + /** + * Make a string uppercase. + * + * EXAMPLE: UTF8::strtoupper('Déjà Σσς Iıİi'); // 'DÉJÀ ΣΣΣ IIİI' + * + * @see http://php.net/manual/en/function.mb-strtoupper.php + * + * @param string $str

The string being uppercased.

+ * @param string $encoding [optional]

Set the charset.

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * @param string|null $lang [optional]

Set the language for special cases: az, el, lt, + * tr

+ * @param bool $try_to_keep_the_string_length [optional]

true === try to keep the string length: e.g. ẞ + * -> ß

+ * + * @psalm-pure + * + * @return string + *

String with all alphabetic characters converted to uppercase.

+ */ + public static function strtoupper( + $str, + string $encoding = 'UTF-8', + bool $clean_utf8 = false, + string $lang = null, + bool $try_to_keep_the_string_length = false + ): string { + // init + $str = (string) $str; + + if ($str === '') { + return ''; + } + + if ($clean_utf8) { + // "mb_strpos()" and "iconv_strpos()" returns wrong position, + // if invalid characters are found in $haystack before $needle + $str = self::clean($str); + } + + // hack for old php version or for the polyfill ... + if ($try_to_keep_the_string_length) { + $str = self::fixStrCaseHelper($str); + } + + if ($lang === null && $encoding === 'UTF-8') { + return \mb_strtoupper($str); + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + if ($lang !== null) { + if (self::$SUPPORT['intl'] === true) { + if (self::$INTL_TRANSLITERATOR_LIST === null) { + self::$INTL_TRANSLITERATOR_LIST = self::getData('transliterator_list'); + } + + $language_code = $lang . '-Upper'; + if (!\in_array($language_code, self::$INTL_TRANSLITERATOR_LIST, true)) { + /** + * @psalm-suppress ImpureFunctionCall - this is only a warning + */ + \trigger_error('UTF8::strtoupper() without intl for special language: ' . $lang, \E_USER_WARNING); + + $language_code = 'Any-Upper'; + } + + return (string) \transliterator_transliterate($language_code, $str); + } + + /** + * @psalm-suppress ImpureFunctionCall - this is only a warning + */ + \trigger_error('UTF8::strtolower() without intl cannot handle the "lang"-parameter: ' . $lang, \E_USER_WARNING); + } + + // always fallback via symfony polyfill + return \mb_strtoupper($str, $encoding); + } + + /** + * Translate characters or replace sub-strings. + * + * EXAMPLE: + * + * $array = [ + * 'Hello' => '○●◎', + * '中文空白' => 'earth', + * ]; + * UTF8::strtr('Hello 中文空白', $array); // '○●◎ earth' + * + * + * @see http://php.net/manual/en/function.strtr.php + * + * @param string $str

The string being translated.

+ * @param string|string[] $from

The string replacing from.

+ * @param string|string[] $to [optional]

The string being translated to to.

+ * + * @psalm-pure + * + * @return string + *

This function returns a copy of str, translating all occurrences of each character in "from" + * to the corresponding character in "to".

+ */ + public static function strtr(string $str, $from, $to = ''): string + { + if ($str === '') { + return ''; + } + + if ($from === $to) { + return $str; + } + + if ($to !== '') { + if (!\is_array($from)) { + $from = self::str_split($from); + } + + if (!\is_array($to)) { + $to = self::str_split($to); + } + + $count_from = \count($from); + $count_to = \count($to); + + if ($count_from !== $count_to) { + if ($count_from > $count_to) { + $from = \array_slice($from, 0, $count_to); + } elseif ($count_from < $count_to) { + $to = \array_slice($to, 0, $count_from); + } + } + + try { + $from = \array_combine($from, $to); + } catch (\Error $e) { + // PHP >= 8.0 : array_combine() will now throw a ValueError if the number of elements for each array is not equal; previously this function returned false instead. + $from = false; + } + if ($from === false) { + throw new \InvalidArgumentException('The number of elements for each array isn\'t equal or the arrays are empty: (from: ' . \print_r($from, true) . ' | to: ' . \print_r($to, true) . ')'); + } + } + + if (\is_string($from)) { + return \str_replace($from, $to, $str); + } + + return \strtr($str, $from); + } + + /** + * Return the width of a string. + * + * INFO: use UTF8::strlen() for the byte-length + * + * EXAMPLE: UTF8::strwidth("Iñtërnâtiôn\xE9àlizætiøn")); // 21 + * + * @param string $str

The input string.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * + * @psalm-pure + * + * @return int + * + * @phpstan-return 0|positive-int + */ + public static function strwidth( + string $str, + string $encoding = 'UTF-8', + bool $clean_utf8 = false + ): int { + if ($str === '') { + return 0; + } + + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + if ($clean_utf8) { + // iconv and mbstring are not tolerant to invalid encoding + // further, their behaviour is inconsistent with that of PHP's substr + $str = self::clean($str); + } + + // + // fallback via mbstring + // + + if (self::$SUPPORT['mbstring'] === true) { + if ($encoding === 'UTF-8') { + return \mb_strwidth($str); + } + + return \mb_strwidth($str, $encoding); + } + + // + // fallback via vanilla php + // + + if ($encoding !== 'UTF-8') { + $str = self::encode('UTF-8', $str, false, $encoding); + } + + $wide = 0; + $str = (string) \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', '', $str, -1, $wide); + + /* @phpstan-ignore-next-line | should return 0|positive-int */ + return ($wide << 1) + (int) self::strlen($str); + } + + /** + * Get part of a string. + * + * EXAMPLE: UTF8::substr('中文空白', 1, 2); // '文空' + * + * @see http://php.net/manual/en/function.mb-substr.php + * + * @param string $str

The string being checked.

+ * @param int $offset

The first position used in str.

+ * @param int|null $length [optional]

The maximum length of the returned string.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * + * @psalm-pure + * + * @return false|string + * The portion of str specified by the offset and + * length parameters.

If str is shorter than offset + * characters long, FALSE will be returned. + */ + public static function substr( + string $str, + int $offset = 0, + int $length = null, + string $encoding = 'UTF-8', + bool $clean_utf8 = false + ) { + // empty string + if ($str === '' || $length === 0) { + return ''; + } + + if ($clean_utf8) { + // iconv and mbstring are not tolerant to invalid encoding + // further, their behaviour is inconsistent with that of PHP's substr + $str = self::clean($str); + } + + // whole string + if (!$offset && $length === null) { + return $str; + } + + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + // + // fallback via mbstring + // + + if (self::$SUPPORT['mbstring'] === true && $encoding === 'UTF-8') { + if ($length === null) { + return \mb_substr($str, $offset); + } + + return \mb_substr($str, $offset, $length); + } + + // + // fallback for binary || ascii only + // + + if ( + $encoding === 'CP850' + || + $encoding === 'ASCII' + ) { + if ($length === null) { + return \substr($str, $offset); + } + + return \substr($str, $offset, $length); + } + + // otherwise we need the string-length + $str_length = 0; + if ( + $offset + || + $length === null /* @phpstan-ignore-line | can be NULL here?! */ + ) { + $str_length = self::strlen($str, $encoding); + } + + // e.g.: invalid chars + mbstring not installed + if ($str_length === false) { + return false; + } + + // empty string + if ($offset === $str_length && !$length) { + return ''; + } + + // impossible + if ($offset && $offset > $str_length) { + return ''; + } + + $length = $length ?? $str_length; + + if ( + $encoding !== 'UTF-8' + && + self::$SUPPORT['mbstring'] === false + ) { + /** + * @psalm-suppress ImpureFunctionCall - this is only a warning + */ + \trigger_error('UTF8::substr() without mbstring cannot handle "' . $encoding . '" encoding', \E_USER_WARNING); + } + + // + // fallback via intl + // + + if ( + $encoding === 'UTF-8' // INFO: "grapheme_substr()" can't handle other encodings + && + $offset >= 0 // grapheme_substr() can't handle negative offset + && + self::$SUPPORT['intl'] === true + ) { + $return_tmp = \grapheme_substr($str, $offset, $length); + if ($return_tmp !== false) { + return $return_tmp; + } + } + + // + // fallback via iconv + // + + if ( + $length >= 0 // "iconv_substr()" can't handle negative length + && + self::$SUPPORT['iconv'] === true + ) { + $return_tmp = \iconv_substr($str, $offset, $length); + if ($return_tmp !== false) { + return $return_tmp; + } + } + + // + // fallback for ascii only + // + + if (ASCII::is_ascii($str)) { + return \substr($str, $offset, $length); + } + + // + // fallback via vanilla php + // + + // split to array, and remove invalid characters + // && + // extract relevant part, and join to make sting again + return \implode('', \array_slice(self::str_split($str), $offset, $length)); + } + + /** + * Binary-safe comparison of two strings from an offset, up to a length of characters. + * + * EXAMPLE: + * UTF8::substr_compare("○●◎\r", '●◎', 0, 2); // -1 + * UTF8::substr_compare("○●◎\r", '◎●', 1, 2); // 1 + * UTF8::substr_compare("○●◎\r", '●◎', 1, 2); // 0 + * + * + * @param string $str1

The main string being compared.

+ * @param string $str2

The secondary string being compared.

+ * @param int $offset [optional]

The start position for the comparison. If negative, it starts + * counting from the end of the string.

+ * @param int|null $length [optional]

The length of the comparison. The default value is the largest + * of the length of the str compared to the length of main_str less the + * offset.

+ * @param bool $case_insensitivity [optional]

If case_insensitivity is TRUE, comparison is case + * insensitive.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return int + * < 0 if str1 is less than str2;
+ * > 0 if str1 is greater than str2,
+ * 0 if they are equal + */ + public static function substr_compare( + string $str1, + string $str2, + int $offset = 0, + int $length = null, + bool $case_insensitivity = false, + string $encoding = 'UTF-8' + ): int { + if ( + $offset !== 0 + || + $length !== null + ) { + if ($encoding === 'UTF-8') { + if ($length === null) { + $str1 = (string) \mb_substr($str1, $offset); + } else { + $str1 = (string) \mb_substr($str1, $offset, $length); + } + $str2 = (string) \mb_substr($str2, 0, (int) self::strlen($str1)); + } else { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + $str1 = (string) self::substr($str1, $offset, $length, $encoding); + $str2 = (string) self::substr($str2, 0, (int) self::strlen($str1), $encoding); + } + } + + if ($case_insensitivity) { + return self::strcasecmp($str1, $str2, $encoding); + } + + return self::strcmp($str1, $str2); + } + + /** + * Count the number of substring occurrences. + * + * EXAMPLE: UTF8::substr_count('中文空白', '文空', 1, 2); // 1 + * + * @see http://php.net/manual/en/function.substr-count.php + * + * @param string $haystack

The string to search in.

+ * @param string $needle

The substring to search for.

+ * @param int $offset [optional]

The offset where to start counting.

+ * @param int|null $length [optional]

+ * The maximum length after the specified offset to search for the + * substring. It outputs a warning if the offset plus the length is + * greater than the haystack length. + *

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * + * @psalm-pure + * + * @return false|int + *

This functions returns an integer or false if there isn't a string.

+ */ + public static function substr_count( + string $haystack, + string $needle, + int $offset = 0, + int $length = null, + string $encoding = 'UTF-8', + bool $clean_utf8 = false + ) { + if ($needle === '') { + return false; + } + + if ($haystack === '' || $length === 0) { + return 0; + } + + if ($encoding !== 'UTF-8' && $encoding !== 'CP850') { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + } + + if ($clean_utf8) { + // "mb_strpos()" and "iconv_strpos()" returns wrong position, + // if invalid characters are found in $haystack before $needle + $needle = self::clean($needle); + $haystack = self::clean($haystack); + } + + if ($offset || $length > 0) { + if ($length === null) { + $length_tmp = self::strlen($haystack, $encoding); + if ($length_tmp === false) { + return false; + } + $length = $length_tmp; + } + + if ($encoding === 'UTF-8') { + $haystack = (string) \mb_substr($haystack, $offset, $length); + } else { + $haystack = (string) \mb_substr($haystack, $offset, $length, $encoding); + } + } + + if ( + $encoding !== 'UTF-8' + && + self::$SUPPORT['mbstring'] === false + ) { + /** + * @psalm-suppress ImpureFunctionCall - this is only a warning + */ + \trigger_error('UTF8::substr_count() without mbstring cannot handle "' . $encoding . '" encoding', \E_USER_WARNING); + } + + if (self::$SUPPORT['mbstring'] === true) { + if ($encoding === 'UTF-8') { + return \mb_substr_count($haystack, $needle); + } + + return \mb_substr_count($haystack, $needle, $encoding); + } + + \preg_match_all('/' . \preg_quote($needle, '/') . '/us', $haystack, $matches, \PREG_SET_ORDER); + + return \count($matches); + } + + /** + * Count the number of substring occurrences. + * + * @param string $haystack

+ * The string being checked. + *

+ * @param string $needle

+ * The string being found. + *

+ * @param int $offset [optional]

+ * The offset where to start counting + *

+ * @param int|null $length [optional]

+ * The maximum length after the specified offset to search for the + * substring. It outputs a warning if the offset plus the length is + * greater than the haystack length. + *

+ * + * @psalm-pure + * + * @return false|int + *

The number of times the + * needle substring occurs in the + * haystack string.

+ */ + public static function substr_count_in_byte( + string $haystack, + string $needle, + int $offset = 0, + int $length = null + ) { + if ($haystack === '' || $needle === '') { + return 0; + } + + if ( + ($offset || $length !== null) + && + self::$SUPPORT['mbstring_func_overload'] === true + ) { + if ($length === null) { + $length_tmp = self::strlen($haystack); + if ($length_tmp === false) { + return false; + } + $length = $length_tmp; + } + + if ( + ( + $length !== 0 + && + $offset !== 0 + ) + && + ($length + $offset) <= 0 + && + \PHP_VERSION_ID < 71000 // output from "substr_count()" have changed in PHP 7.1 + ) { + return false; + } + + /** @var false|string $haystack_tmp - needed for PhpStan (stubs error) */ + $haystack_tmp = \substr($haystack, $offset, $length); + if ($haystack_tmp === false) { + $haystack_tmp = ''; + } + $haystack = (string) $haystack_tmp; + } + + if (self::$SUPPORT['mbstring_func_overload'] === true) { + // "mb_" is available if overload is used, so use it ... + return \mb_substr_count($haystack, $needle, 'CP850'); // 8-BIT + } + + if ($length === null) { + return \substr_count($haystack, $needle, $offset); + } + + return \substr_count($haystack, $needle, $offset, $length); + } + + /** + * Returns the number of occurrences of $substring in the given string. + * By default, the comparison is case-sensitive, but can be made insensitive + * by setting $case_sensitive to false. + * + * @param string $str

The input string.

+ * @param string $substring

The substring to search for.

+ * @param bool $case_sensitive [optional]

Whether or not to enforce case-sensitivity. Default: true

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return int + * + * @phpstan-return 0|positive-int + */ + public static function substr_count_simple( + string $str, + string $substring, + bool $case_sensitive = true, + string $encoding = 'UTF-8' + ): int { + if ($str === '' || $substring === '') { + return 0; + } + + if ($encoding === 'UTF-8') { + if ($case_sensitive) { + return (int) \mb_substr_count($str, $substring); + } + + return (int) \mb_substr_count( + \mb_strtoupper($str), + \mb_strtoupper($substring) + ); + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + if ($case_sensitive) { + return (int) \mb_substr_count($str, $substring, $encoding); + } + + return (int) \mb_substr_count( + self::strtocasefold($str, true, false, $encoding, null, false), + self::strtocasefold($substring, true, false, $encoding, null, false), + $encoding + ); + } + + /** + * Removes a prefix ($needle) from the beginning of the string ($haystack), case-insensitive. + * + * EXMAPLE: + * UTF8::substr_ileft('ΚόσμεMiddleEnd', 'Κόσμε'); // 'MiddleEnd' + * UTF8::substr_ileft('ΚόσμεMiddleEnd', 'κόσμε'); // 'MiddleEnd' + * + * + * @param string $haystack

The string to search in.

+ * @param string $needle

The substring to search for.

+ * + * @psalm-pure + * + * @return string + *

Return the sub-string.

+ */ + public static function substr_ileft(string $haystack, string $needle): string + { + if ($haystack === '') { + return ''; + } + + if ($needle === '') { + return $haystack; + } + + if (self::str_istarts_with($haystack, $needle)) { + $haystack = (string) \mb_substr($haystack, (int) self::strlen($needle)); + } + + return $haystack; + } + + /** + * Get part of a string process in bytes. + * + * @param string $str

The string being checked.

+ * @param int $offset

The first position used in str.

+ * @param int|null $length [optional]

The maximum length of the returned string.

+ * + * @psalm-pure + * + * @return false|string + *

The portion of str specified by the offset and + * length parameters.

If str is shorter than offset + * characters long, FALSE will be returned.

+ */ + public static function substr_in_byte(string $str, int $offset = 0, int $length = null) + { + // empty string + if ($str === '' || $length === 0) { + return ''; + } + + // whole string + if (!$offset && $length === null) { + return $str; + } + + if (self::$SUPPORT['mbstring_func_overload'] === true) { + // "mb_" is available if overload is used, so use it ... + return \mb_substr($str, $offset, $length, 'CP850'); // 8-BIT + } + + return \substr($str, $offset, $length ?? 2147483647); + } + + /** + * Removes a suffix ($needle) from the end of the string ($haystack), case-insensitive. + * + * EXAMPLE: + * UTF8::substr_iright('BeginMiddleΚόσμε', 'Κόσμε'); // 'BeginMiddle' + * UTF8::substr_iright('BeginMiddleΚόσμε', 'κόσμε'); // 'BeginMiddle' + * + * + * @param string $haystack

The string to search in.

+ * @param string $needle

The substring to search for.

+ * + * @psalm-pure + * + * @return string + *

Return the sub-string.

+ */ + public static function substr_iright(string $haystack, string $needle): string + { + if ($haystack === '') { + return ''; + } + + if ($needle === '') { + return $haystack; + } + + if (self::str_iends_with($haystack, $needle)) { + $haystack = (string) \mb_substr($haystack, 0, (int) self::strlen($haystack) - (int) self::strlen($needle)); + } + + return $haystack; + } + + /** + * Removes a prefix ($needle) from the beginning of the string ($haystack). + * + * EXAMPLE: + * UTF8::substr_left('ΚόσμεMiddleEnd', 'Κόσμε'); // 'MiddleEnd' + * UTF8::substr_left('ΚόσμεMiddleEnd', 'κόσμε'); // 'ΚόσμεMiddleEnd' + * + * + * @param string $haystack

The string to search in.

+ * @param string $needle

The substring to search for.

+ * + * @psalm-pure + * + * @return string + *

Return the sub-string.

+ */ + public static function substr_left(string $haystack, string $needle): string + { + if ($haystack === '') { + return ''; + } + + if ($needle === '') { + return $haystack; + } + + if (self::str_starts_with($haystack, $needle)) { + $haystack = (string) \mb_substr($haystack, (int) self::strlen($needle)); + } + + return $haystack; + } + + /** + * Replace text within a portion of a string. + * + * EXAMPLE: UTF8::substr_replace(array('Iñtërnâtiônàlizætiøn', 'foo'), 'æ', 1); // array('Iæñtërnâtiônàlizætiøn', 'fæoo') + * + * source: https://gist.github.com/stemar/8287074 + * + * @param string|string[] $str

The input string or an array of stings.

+ * @param string|string[] $replacement

The replacement string or an array of stings.

+ * @param int|int[] $offset

+ * If start is positive, the replacing will begin at the start'th offset + * into string. + *

+ * If start is negative, the replacing will begin at the start'th character + * from the end of string. + *

+ * @param int|int[]|null $length [optional]

If given and is positive, it represents the length of the + * portion of string which is to be replaced. If it is negative, it + * represents the number of characters from the end of string at which to + * stop replacing. If it is not given, then it will default to strlen( + * string ); i.e. end the replacing at the end of string. Of course, if + * length is zero then this function will have the effect of inserting + * replacement into string at the given start offset.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string|string[] + *

The result string is returned. If string is an array then array is returned.

+ * + * @template TSubstrReplace string|string[] + * @phpstan-param TSubstrReplace $str + * @phpstan-return TSubstrReplace + */ + public static function substr_replace( + $str, + $replacement, + $offset, + $length = null, + string $encoding = 'UTF-8' + ) { + if (\is_array($str)) { + $num = \count($str); + + // the replacement + if (\is_array($replacement)) { + $replacement = \array_slice($replacement, 0, $num); + } else { + $replacement = \array_pad([$replacement], $num, $replacement); + } + + // the offset + if (\is_array($offset)) { + $offset = \array_slice($offset, 0, $num); + foreach ($offset as &$value_tmp) { + $value_tmp = (int) $value_tmp === $value_tmp ? $value_tmp : 0; + } + unset($value_tmp); + } else { + $offset = \array_pad([$offset], $num, $offset); + } + + // the length + if ($length === null) { + $length = \array_fill(0, $num, 0); + } elseif (\is_array($length)) { + $length = \array_slice($length, 0, $num); + foreach ($length as &$value_tmp_V2) { + $value_tmp_V2 = (int) $value_tmp_V2 === $value_tmp_V2 ? $value_tmp_V2 : $num; + } + unset($value_tmp_V2); + } else { + $length = \array_pad([$length], $num, $length); + } + + // recursive call + /** @phpstan-ignore-next-line - phpstan currently can't handle recursive calls */ + return \array_map([self::class, 'substr_replace'], $str, $replacement, $offset, $length); + } + + if (\is_array($replacement)) { + if ($replacement !== []) { + $replacement = $replacement[0]; + } else { + $replacement = ''; + } + } + + // init + $str = (string) $str; + $replacement = (string) $replacement; + + if (\is_array($length)) { + throw new \InvalidArgumentException('Parameter "$length" can only be an array, if "$str" is also an array.'); + } + + if (\is_array($offset)) { + throw new \InvalidArgumentException('Parameter "$offset" can only be an array, if "$str" is also an array.'); + } + + if ($str === '') { + return $replacement; + } + + if (self::$SUPPORT['mbstring'] === true) { + $string_length = (int) self::strlen($str, $encoding); + + if ($offset < 0) { + $offset = (int) \max(0, $string_length + $offset); + } elseif ($offset > $string_length) { + $offset = $string_length; + } + + if ($length !== null && $length < 0) { + $length = (int) \max(0, $string_length - $offset + $length); + } elseif ($length === null || $length > $string_length) { + $length = $string_length; + } + + if (($offset + $length) > $string_length) { + $length = $string_length - $offset; + } + + return ((string) \mb_substr($str, 0, $offset, $encoding)) . + $replacement . + ((string) \mb_substr($str, $offset + $length, $string_length - $offset - $length, $encoding)); + } + + // + // fallback for ascii only + // + + if (ASCII::is_ascii($str)) { + return ($length === null) ? + \substr_replace($str, $replacement, $offset) : + \substr_replace($str, $replacement, $offset, $length); + } + + // + // fallback via vanilla php + // + + \preg_match_all('/./us', $str, $str_matches); + \preg_match_all('/./us', $replacement, $replacement_matches); + + if ($length === null) { + $length_tmp = self::strlen($str, $encoding); + if ($length_tmp === false) { + // e.g.: non mbstring support + invalid chars + return ''; + } + $length = $length_tmp; + } + + \array_splice($str_matches[0], $offset, $length, $replacement_matches[0]); + + return \implode('', $str_matches[0]); + } + + /** + * Removes a suffix ($needle) from the end of the string ($haystack). + * + * EXAMPLE: + * UTF8::substr_right('BeginMiddleΚόσμε', 'Κόσμε'); // 'BeginMiddle' + * UTF8::substr_right('BeginMiddleΚόσμε', 'κόσμε'); // 'BeginMiddleΚόσμε' + * + * + * @param string $haystack

The string to search in.

+ * @param string $needle

The substring to search for.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * + * @psalm-pure + * + * @return string + *

Return the sub-string.

+ */ + public static function substr_right( + string $haystack, + string $needle, + string $encoding = 'UTF-8' + ): string { + if ($haystack === '') { + return ''; + } + + if ($needle === '') { + return $haystack; + } + + if ( + $encoding === 'UTF-8' + && + \substr($haystack, -\strlen($needle)) === $needle + ) { + return (string) \mb_substr($haystack, 0, (int) \mb_strlen($haystack) - (int) \mb_strlen($needle)); + } + + if (\substr($haystack, -\strlen($needle)) === $needle) { + return (string) self::substr( + $haystack, + 0, + (int) self::strlen($haystack, $encoding) - (int) self::strlen($needle, $encoding), + $encoding + ); + } + + return $haystack; + } + + /** + * Returns a case swapped version of the string. + * + * EXAMPLE: UTF8::swapCase('déJÀ σσς iıII'); // 'DÉjà ΣΣΣ IIii' + * + * @param string $str

The input string.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * + * @psalm-pure + * + * @return string + *

Each character's case swapped.

+ */ + public static function swapCase(string $str, string $encoding = 'UTF-8', bool $clean_utf8 = false): string + { + if ($str === '') { + return ''; + } + + if ($clean_utf8) { + // "mb_strpos()" and "iconv_strpos()" returns wrong position, + // if invalid characters are found in $haystack before $needle + $str = self::clean($str); + } + + if ($encoding === 'UTF-8') { + return (string) (\mb_strtolower($str) ^ \mb_strtoupper($str) ^ $str); + } + + return (string) (self::strtolower($str, $encoding) ^ self::strtoupper($str, $encoding) ^ $str); + } + + /** + * Checks whether symfony-polyfills are used. + * + * @psalm-pure + * + * @return bool + *

true if in use, false otherwise

+ * + * @internal

Please do not use it anymore, we will make is private in next major version.

+ */ + public static function symfony_polyfill_used(): bool + { + // init + $return = false; + + $return_tmp = \extension_loaded('mbstring'); + if (!$return_tmp && \function_exists('mb_strlen')) { + $return = true; + } + + $return_tmp = \extension_loaded('iconv'); + if (!$return_tmp && \function_exists('iconv')) { + $return = true; + } + + return $return; + } + + /** + * @param string $str + * @param int $tab_length + * + * @psalm-pure + * + * @return string + */ + public static function tabs_to_spaces(string $str, int $tab_length = 4): string + { + if ($tab_length === 4) { + $spaces = ' '; + } elseif ($tab_length === 2) { + $spaces = ' '; + } else { + $spaces = \str_repeat(' ', $tab_length); + } + + return \str_replace("\t", $spaces, $str); + } + + /** + * Converts the first character of each word in the string to uppercase + * and all other chars to lowercase. + * + * @param string $str

The input string.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * @param string|null $lang [optional]

Set the language for special cases: az, el, lt, + * tr

+ * @param bool $try_to_keep_the_string_length [optional]

true === try to keep the string length: e.g. ẞ + * -> ß

+ * + * @psalm-pure + * + * @return string + *

A string with all characters of $str being title-cased.

+ */ + public static function titlecase( + string $str, + string $encoding = 'UTF-8', + bool $clean_utf8 = false, + string $lang = null, + bool $try_to_keep_the_string_length = false + ): string { + if ($clean_utf8) { + // "mb_strpos()" and "iconv_strpos()" returns wrong position, + // if invalid characters are found in $haystack before $needle + $str = self::clean($str); + } + + if ( + $lang === null + && + !$try_to_keep_the_string_length + ) { + if ($encoding === 'UTF-8') { + return \mb_convert_case($str, \MB_CASE_TITLE); + } + + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + return \mb_convert_case($str, \MB_CASE_TITLE, $encoding); + } + + return self::str_titleize( + $str, + null, + $encoding, + false, + $lang, + $try_to_keep_the_string_length, + false + ); + } + + /** + * Convert a string into ASCII. + * + * EXAMPLE: UTF8::to_ascii('déjà σσς iıii'); // 'deja sss iiii' + * + * @param string $str

The input string.

+ * @param string $unknown [optional]

Character use if character unknown. (default is ?)

+ * @param bool $strict [optional]

Use "transliterator_transliterate()" from PHP-Intl | WARNING: bad + * performance

+ * + * @psalm-pure + * + * @return string + */ + public static function to_ascii( + string $str, + string $unknown = '?', + bool $strict = false + ): string { + return ASCII::to_transliterate($str, $unknown, $strict); + } + + /** + * @param bool|float|int|string $str + * + * @psalm-pure + * + * @return bool + */ + public static function to_boolean($str): bool + { + // init + $str = (string) $str; + + if ($str === '') { + return false; + } + + // Info: http://php.net/manual/en/filter.filters.validate.php + $map = [ + 'true' => true, + '1' => true, + 'on' => true, + 'yes' => true, + 'false' => false, + '0' => false, + 'off' => false, + 'no' => false, + ]; + + if (isset($map[$str])) { + return $map[$str]; + } + + $key = \strtolower($str); + if (isset($map[$key])) { + return $map[$key]; + } + + if (\is_numeric($str)) { + return ((float) $str) > 0; + } + + return (bool) \trim($str); + } + + /** + * Convert given string to safe filename (and keep string case). + * + * @param string $str + * @param bool $use_transliterate No transliteration, conversion etc. is done by default - unsafe characters are + * simply replaced with hyphen. + * @param string $fallback_char + * + * @psalm-pure + * + * @return string + */ + public static function to_filename( + string $str, + bool $use_transliterate = false, + string $fallback_char = '-' + ): string { + return ASCII::to_filename( + $str, + $use_transliterate, + $fallback_char + ); + } + + /** + * Convert a string into "ISO-8859"-encoding (Latin-1). + * + * EXAMPLE: UTF8::to_utf8(UTF8::to_iso8859(' -ABC-中文空白- ')); // ' -ABC-????- ' + * + * @param string|string[] $str + * + * @psalm-pure + * + * @return string|string[] + * + * @template TToIso8859 as string|string[] + * @phpstan-param TToIso8859 $str + * @phpstan-return (TToIso8859 is string ? string : string[]) + */ + public static function to_iso8859($str) + { + if (\is_array($str)) { + foreach ($str as &$v) { + $v = self::to_iso8859($v); + } + + return $str; + } + + /* @phpstan-ignore-next-line | FP? -> "Cannot cast TToIso8859 of array|string to string." it's a string here */ + $str = (string) $str; + if ($str === '') { + return ''; + } + + return self::utf8_decode($str); + } + + /** + * This function leaves UTF-8 characters alone, while converting almost all non-UTF8 to UTF8. + * + *
    + *
  • It decode UTF-8 codepoints and Unicode escape sequences.
  • + *
  • It assumes that the encoding of the original string is either WINDOWS-1252 or ISO-8859.
  • + *
  • WARNING: It does not remove invalid UTF-8 characters, so you maybe need to use "UTF8::clean()" for this + * case.
  • + *
+ * + * EXAMPLE: UTF8::to_utf8(["\u0063\u0061\u0074"]); // array('cat') + * + * @param string|string[] $str

Any string or array of strings.

+ * @param bool $decode_html_entity_to_utf8

Set to true, if you need to decode html-entities.

+ * + * @psalm-pure + * + * @return string|string[] + *

The UTF-8 encoded string

+ * + * @template TToUtf8 as string|string[] + * @phpstan-param TToUtf8 $str + * @phpstan-return (TToUtf8 is string ? string : string[]) + */ + public static function to_utf8($str, bool $decode_html_entity_to_utf8 = false) + { + if (\is_array($str)) { + foreach ($str as &$v) { + $v = self::to_utf8_string($v, $decode_html_entity_to_utf8); + } + + /** @phpstan-var TToUtf8 $str */ + return $str; + } + + \assert(\is_string($str)); + + $str = self::to_utf8_string($str, $decode_html_entity_to_utf8); + + /** @phpstan-var TToUtf8 $str */ + return $str; + } + + /** + * This function leaves UTF-8 characters alone, while converting almost all non-UTF8 to UTF8. + * + *
    + *
  • It decode UTF-8 codepoints and Unicode escape sequences.
  • + *
  • It assumes that the encoding of the original string is either WINDOWS-1252 or ISO-8859.
  • + *
  • WARNING: It does not remove invalid UTF-8 characters, so you maybe need to use "UTF8::clean()" for this + * case.
  • + *
+ * + * EXAMPLE: UTF8::to_utf8_string("\u0063\u0061\u0074"); // 'cat' + * + * @param string $str

Any string.

+ * @param bool $decode_html_entity_to_utf8

Set to true, if you need to decode html-entities.

+ * + * @psalm-pure + * + * @return string + *

The UTF-8 encoded string

+ * + * @template T as string + * @phpstan-param T $str + * @phpstan-return (T is non-empty-string ? non-empty-string : string) + */ + public static function to_utf8_string(string $str, bool $decode_html_entity_to_utf8 = false): string + { + if ($str === '') { + return $str; + } + + $max = \strlen($str); + $buf = ''; + + for ($i = 0; $i < $max; ++$i) { + $c1 = $str[$i]; + + if ($c1 >= "\xC0") { // should be converted to UTF8, if it's not UTF8 already + + if ($c1 <= "\xDF") { // looks like 2 bytes UTF8 + + $c2 = $i + 1 >= $max ? "\x00" : $str[$i + 1]; + + if ($c2 >= "\x80" && $c2 <= "\xBF") { // yeah, almost sure it's UTF8 already + $buf .= $c1 . $c2; + ++$i; + } else { // not valid UTF8 - convert it + $buf .= self::to_utf8_convert_helper($c1); + } + } elseif ($c1 >= "\xE0" && $c1 <= "\xEF") { // looks like 3 bytes UTF8 + + $c2 = $i + 1 >= $max ? "\x00" : $str[$i + 1]; + $c3 = $i + 2 >= $max ? "\x00" : $str[$i + 2]; + + if ($c2 >= "\x80" && $c2 <= "\xBF" && $c3 >= "\x80" && $c3 <= "\xBF") { // yeah, almost sure it's UTF8 already + $buf .= $c1 . $c2 . $c3; + $i += 2; + } else { // not valid UTF8 - convert it + $buf .= self::to_utf8_convert_helper($c1); + } + } elseif ($c1 >= "\xF0" && $c1 <= "\xF7") { // looks like 4 bytes UTF8 + + $c2 = $i + 1 >= $max ? "\x00" : $str[$i + 1]; + $c3 = $i + 2 >= $max ? "\x00" : $str[$i + 2]; + $c4 = $i + 3 >= $max ? "\x00" : $str[$i + 3]; + + if ($c2 >= "\x80" && $c2 <= "\xBF" && $c3 >= "\x80" && $c3 <= "\xBF" && $c4 >= "\x80" && $c4 <= "\xBF") { // yeah, almost sure it's UTF8 already + $buf .= $c1 . $c2 . $c3 . $c4; + $i += 3; + } else { // not valid UTF8 - convert it + $buf .= self::to_utf8_convert_helper($c1); + } + } else { // doesn't look like UTF8, but should be converted + + $buf .= self::to_utf8_convert_helper($c1); + } + } elseif (($c1 & "\xC0") === "\x80") { // needs conversion + + $buf .= self::to_utf8_convert_helper($c1); + } else { // it doesn't need conversion + + $buf .= $c1; + } + } + + // decode unicode escape sequences + unicode surrogate pairs + $buf = \preg_replace_callback( + '/\\\\u([dD][89abAB][0-9a-fA-F]{2})\\\\u([dD][cdefCDEF][\da-fA-F]{2})|\\\\u([0-9a-fA-F]{4})/', + /** + * @param array $matches + * + * @psalm-pure + * + * @return string + */ + static function (array $matches): string { + if (isset($matches[3])) { + $cp = (int) \hexdec($matches[3]); + } else { + // http://unicode.org/faq/utf_bom.html#utf16-4 + $cp = ((int) \hexdec($matches[1]) << 10) + + (int) \hexdec($matches[2]) + + 0x10000 + - (0xD800 << 10) + - 0xDC00; + } + + // https://github.com/php/php-src/blob/php-7.3.2/ext/standard/html.c#L471 + // + // php_utf32_utf8(unsigned char *buf, unsigned k) + + if ($cp < 0x80) { + return (string) self::chr($cp); + } + + if ($cp < 0xA0) { + /** @noinspection UnnecessaryCastingInspection */ + return (string) self::chr(0xC0 | $cp >> 6) . (string) self::chr(0x80 | $cp & 0x3F); + } + + return self::decimal_to_chr($cp); + }, + $buf + ); + + if ($buf === null) { + return ''; + } + + // decode UTF-8 codepoints + if ($decode_html_entity_to_utf8) { + $buf = self::html_entity_decode($buf); + } + + return $buf; + } + + /** + * Returns the given string as an integer, or null if the string isn't numeric. + * + * @param string $str + * + * @psalm-pure + * + * @return int|null + *

null if the string isn't numeric

+ */ + public static function to_int(string $str) + { + if (\is_numeric($str)) { + return (int) $str; + } + + return null; + } + + /** + * Returns the given input as string, or null if the input isn't int|float|string + * and do not implement the "__toString()" method. + * + * @param float|int|object|string|null $input + * + * @psalm-pure + * + * @return string|null + *

null if the input isn't int|float|string and has no "__toString()" method

+ */ + public static function to_string($input) + { + if ($input === null) { + return null; + } + + $input_type = \gettype($input); + + if ( + $input_type === 'string' + || + $input_type === 'integer' + || + $input_type === 'float' + || + $input_type === 'double' + ) { + /* @phpstan-ignore-next-line | "gettype" is not supported by phpstan?! */ + return (string) $input; + } + + /** @phpstan-ignore-next-line - "gettype": FP? */ + if ($input_type === 'object' && \method_exists($input, '__toString')) { + return (string) $input; + } + + return null; + } + + /** + * Strip whitespace or other characters from the beginning and end of a UTF-8 string. + * + * INFO: This is slower then "trim()" + * + * We can only use the original-function, if we use <= 7-Bit in the string / chars + * but the check for ASCII (7-Bit) cost more time, then we can safe here. + * + * EXAMPLE: UTF8::trim(' -ABC-中文空白- '); // '-ABC-中文空白-' + * + * @param string $str

The string to be trimmed

+ * @param string|null $chars [optional]

Optional characters to be stripped

+ * + * @psalm-pure + * + * @return string + *

The trimmed string.

+ */ + public static function trim(string $str = '', string $chars = null): string + { + if ($str === '') { + return ''; + } + + if (self::$SUPPORT['mbstring'] === true) { + if ($chars !== null) { + /** @noinspection PregQuoteUsageInspection */ + $chars = \preg_quote($chars); + $pattern = "^[{$chars}]+|[{$chars}]+\$"; + } else { + $pattern = '^[\\s]+|[\\s]+$'; + } + + return (string) \mb_ereg_replace($pattern, '', $str); + } + + if ($chars !== null) { + $chars = \preg_quote($chars, '/'); + $pattern = "^[{$chars}]+|[{$chars}]+\$"; + } else { + $pattern = '^[\\s]+|[\\s]+$'; + } + + return self::regex_replace($str, $pattern, ''); + } + + /** + * Makes string's first char uppercase. + * + * EXAMPLE: UTF8::ucfirst('ñtërnâtiônàlizætiøn foo'); // 'Ñtërnâtiônàlizætiøn foo' + * + * @param string $str

The input string.

+ * @param string $encoding [optional]

Set the charset for e.g. "mb_" function

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * @param string|null $lang [optional]

Set the language for special cases: az, el, lt, + * tr

+ * @param bool $try_to_keep_the_string_length [optional]

true === try to keep the string length: e.g. ẞ + * -> ß

+ * + * @psalm-pure + * + * @return string + *

The resulting string with with char uppercase.

+ */ + public static function ucfirst( + string $str, + string $encoding = 'UTF-8', + bool $clean_utf8 = false, + string $lang = null, + bool $try_to_keep_the_string_length = false + ): string { + if ($str === '') { + return ''; + } + + if ($clean_utf8) { + // "mb_strpos()" and "iconv_strpos()" returns wrong position, + // if invalid characters are found in $haystack before $needle + $str = self::clean($str); + } + + $use_mb_functions = $lang === null && !$try_to_keep_the_string_length; + + if ($encoding === 'UTF-8') { + $str_part_two = (string) \mb_substr($str, 1); + + if ($use_mb_functions) { + $str_part_one = \mb_strtoupper( + (string) \mb_substr($str, 0, 1) + ); + } else { + $str_part_one = self::strtoupper( + (string) \mb_substr($str, 0, 1), + $encoding, + false, + $lang, + $try_to_keep_the_string_length + ); + } + } else { + $encoding = self::normalize_encoding($encoding, 'UTF-8'); + + $str_part_two = (string) self::substr($str, 1, null, $encoding); + + if ($use_mb_functions) { + $str_part_one = \mb_strtoupper( + (string) \mb_substr($str, 0, 1, $encoding), + $encoding + ); + } else { + $str_part_one = self::strtoupper( + (string) self::substr($str, 0, 1, $encoding), + $encoding, + false, + $lang, + $try_to_keep_the_string_length + ); + } + } + + return $str_part_one . $str_part_two; + } + + /** + * Uppercase for all words in the string. + * + * EXAMPLE: UTF8::ucwords('iñt ërn âTi ônà liz æti øn'); // 'Iñt Ërn ÂTi Ônà Liz Æti Øn' + * + * @param string $str

The input string.

+ * @param string[] $exceptions [optional]

Exclusion for some words.

+ * @param string $char_list [optional]

Additional chars that contains to words and do not start a new + * word.

+ * @param string $encoding [optional]

Set the charset.

+ * @param bool $clean_utf8 [optional]

Remove non UTF-8 chars from the string.

+ * + * @psalm-pure + * + * @return string + */ + public static function ucwords( + string $str, + array $exceptions = [], + string $char_list = '', + string $encoding = 'UTF-8', + bool $clean_utf8 = false + ): string { + if (!$str) { + return ''; + } + + // INFO: mb_convert_case($str, MB_CASE_TITLE); + // -> MB_CASE_TITLE didn't only uppercase the first letter, it also lowercase all other letters + + if ($clean_utf8) { + // "mb_strpos()" and "iconv_strpos()" returns wrong position, + // if invalid characters are found in $haystack before $needle + $str = self::clean($str); + } + + $use_php_default_functions = !(bool) ($char_list . \implode('', $exceptions)); + + if ( + $use_php_default_functions + && + ASCII::is_ascii($str) + ) { + return \ucwords($str); + } + + $words = self::str_to_words($str, $char_list); + $use_exceptions = $exceptions !== []; + + $words_str = ''; + foreach ($words as &$word) { + if (!$word) { + continue; + } + + if ( + !$use_exceptions + || + !\in_array($word, $exceptions, true) + ) { + $words_str .= self::ucfirst($word, $encoding); + } else { + $words_str .= $word; + } + } + + return $words_str; + } + + /** + * Multi decode HTML entity + fix urlencoded-win1252-chars. + * + * EXAMPLE: UTF8::urldecode('tes%20öäü%20\u00edtest+test'); // 'tes öäü ítest test' + * + * e.g: + * 'test+test' => 'test test' + * 'Düsseldorf' => 'Düsseldorf' + * 'D%FCsseldorf' => 'Düsseldorf' + * 'Düsseldorf' => 'Düsseldorf' + * 'D%26%23xFC%3Bsseldorf' => 'Düsseldorf' + * 'Düsseldorf' => 'Düsseldorf' + * 'D%C3%BCsseldorf' => 'Düsseldorf' + * 'D%C3%83%C2%BCsseldorf' => 'Düsseldorf' + * 'D%25C3%2583%25C2%25BCsseldorf' => 'Düsseldorf' + * + * @param string $str

The input string.

+ * @param bool $multi_decode

Decode as often as possible.

+ * + * @psalm-pure + * + * @return string + * + * @template T as string + * @phpstan-param T $str + * @phpstan-return (T is non-empty-string ? non-empty-string : string) + */ + public static function urldecode(string $str, bool $multi_decode = true): string + { + if ($str === '') { + return ''; + } + + $str = self::urldecode_unicode_helper($str); + + if ($multi_decode) { + do { + $str_compare = $str; + + /** + * @psalm-suppress PossiblyInvalidArgument + */ + $str = \urldecode( + self::html_entity_decode( + self::to_utf8($str), + \ENT_QUOTES | \ENT_HTML5 + ) + ); + } while ($str_compare !== $str); + } else { + /** + * @psalm-suppress PossiblyInvalidArgument + */ + $str = \urldecode( + self::html_entity_decode( + self::to_utf8($str), + \ENT_QUOTES | \ENT_HTML5 + ) + ); + } + + return self::fix_simple_utf8($str); + } + + /** + * Decodes a UTF-8 string to ISO-8859-1. + * + * EXAMPLE: UTF8::encode('UTF-8', UTF8::utf8_decode('-ABC-中文空白-')); // '-ABC-????-' + * + * @param string $str

The input string.

+ * @param bool $keep_utf8_chars + * + * @psalm-pure + * + * @return string + */ + public static function utf8_decode(string $str, bool $keep_utf8_chars = false): string + { + if ($str === '') { + return ''; + } + + // save for later comparision + $str_backup = $str; + $len = \strlen($str); + + if (self::$ORD === null) { + self::$ORD = self::getData('ord'); + } + + if (self::$CHR === null) { + self::$CHR = self::getData('chr'); + } + + $no_char_found = '?'; + for ($i = 0, $j = 0; $i < $len; ++$i, ++$j) { + switch ($str[$i] & "\xF0") { + case "\xC0": + case "\xD0": + $c = (self::$ORD[$str[$i] & "\x1F"] << 6) | self::$ORD[$str[++$i] & "\x3F"]; + $str[$j] = $c < 256 ? self::$CHR[$c] : $no_char_found; + + break; + + case "\xF0": + ++$i; + + // no break + + case "\xE0": + $str[$j] = $no_char_found; + $i += 2; + + break; + + default: + $str[$j] = $str[$i]; + } + } + + /** @var false|string $return - needed for PhpStan (stubs error) */ + $return = \substr($str, 0, $j); + if ($return === false) { + $return = ''; + } + + if ( + $keep_utf8_chars + && + (int) self::strlen($return) >= (int) self::strlen($str_backup) + ) { + return $str_backup; + } + + return $return; + } + + /** + * Encodes an ISO-8859-1 string to UTF-8. + * + * EXAMPLE: UTF8::utf8_decode(UTF8::utf8_encode('-ABC-中文空白-')); // '-ABC-中文空白-' + * + * @param string $str

The input string.

+ * + * @psalm-pure + * + * @return string + */ + public static function utf8_encode(string $str): string + { + if ($str === '') { + return ''; + } + + /** @noinspection PhpUsageOfSilenceOperatorInspection | TODO for PHP > 8.2: find a replacement for this */ + /** @var false|string $str - the polyfill maybe return false */ + $str = @\utf8_encode($str); + + if ($str === false) { + return ''; + } + + return $str; + } + + /** + * Returns an array with all utf8 whitespace characters. + * + * @see http://www.bogofilter.org/pipermail/bogofilter/2003-March/001889.html + * + * @psalm-pure + * + * @return string[] + * An array with all known whitespace characters as values and the type of whitespace as keys + * as defined in above URL + */ + public static function whitespace_table(): array + { + return self::$WHITESPACE_TABLE; + } + + /** + * Limit the number of words in a string. + * + * EXAMPLE: UTF8::words_limit('fòô bàř fòô', 2, ''); // 'fòô bàř' + * + * @param string $str

The input string.

+ * @param int<1, max> $limit

The limit of words as integer.

+ * @param string $str_add_on

Replacement for the striped string.

+ * + * @psalm-pure + * + * @return string + */ + public static function words_limit( + string $str, + int $limit = 100, + string $str_add_on = '…' + ): string { + if ( + $str === '' + || + /* @phpstan-ignore-next-line | we do not trust the phpdoc check */ + $limit <= 0 + ) { + return ''; + } + + \preg_match('/^\\s*+(?:[^\\s]++\\s*+){1,' . $limit . '}/u', $str, $matches); + + if ( + !isset($matches[0]) + || + \mb_strlen($str) === (int) \mb_strlen($matches[0]) + ) { + return $str; + } + + return \rtrim($matches[0]) . $str_add_on; + } + + /** + * Wraps a string to a given number of characters + * + * EXAMPLE: UTF8::wordwrap('Iñtërnâtiônàlizætiøn', 2, '
', true)); // 'Iñ

rn
ât


li

ti
øn'
+ * + * @see http://php.net/manual/en/function.wordwrap.php + * + * @param string $str

The input string.

+ * @param int<1, max> $width [optional]

The column width.

+ * @param string $break [optional]

The line is broken using the optional break parameter.

+ * @param bool $cut [optional]

+ * If the cut is set to true, the string is + * always wrapped at or before the specified width. So if you have + * a word that is larger than the given width, it is broken apart. + *

+ * + * @psalm-pure + * + * @return string + *

The given string wrapped at the specified column.

+ */ + public static function wordwrap( + string $str, + int $width = 75, + string $break = "\n", + bool $cut = false + ): string { + if ($str === '' || $break === '') { + return ''; + } + + $str_split = \explode($break, $str); + + /** @var string[] $charsArray */ + $charsArray = []; + $word_split = ''; + foreach ($str_split as $i => $i_value) { + if ($i) { + $charsArray[] = $break; + $word_split .= '#'; + } + + foreach (self::str_split($i_value) as $c) { + $charsArray[] = $c; + if ($c === ' ') { + $word_split .= ' '; + } else { + $word_split .= '?'; + } + } + } + + $str_return = ''; + $j = 0; + $b = -1; + $i = -1; + $word_split = \wordwrap($word_split, $width, '#', $cut); + + $max = \mb_strlen($word_split); + /** @noinspection PhpAssignmentInConditionInspection - is ok here */ + while (($b = \mb_strpos($word_split, '#', $b + 1)) !== false) { + for (++$i; $i < $b; ++$i) { + if (isset($charsArray[$j])) { + $str_return .= $charsArray[$j]; + unset($charsArray[$j]); + } + ++$j; + + // prevent endless loop, e.g. if there is a error in the "mb_*" polyfill + if ($i > $max) { + break 2; + } + } + + if ( + $break === $charsArray[$j] + || + $charsArray[$j] === ' ' + ) { + unset($charsArray[$j++]); + } + + $str_return .= $break; + + // prevent endless loop, e.g. if there is a error in the "mb_*" polyfill + if ($b > $max) { + break; + } + } + + return $str_return . \implode('', $charsArray); + } + + /** + * Line-Wrap the string after $limit, but split the string by "$delimiter" before ... + * ... so that we wrap the per line. + * + * @param string $str

The input string.

+ * @param int<1, max> $width [optional]

The column width.

+ * @param string $break [optional]

The line is broken using the optional break parameter.

+ * @param bool $cut [optional]

+ * If the cut is set to true, the string is + * always wrapped at or before the specified width. So if you have + * a word that is larger than the given width, it is broken apart. + *

+ * @param bool $add_final_break [optional]

+ * If this flag is true, then the method will add a $break at the end + * of the result string. + *

+ * @param non-empty-string|null $delimiter [optional]

+ * You can change the default behavior, where we split the string by newline. + *

+ * + * @psalm-pure + * + * @return string + */ + public static function wordwrap_per_line( + string $str, + int $width = 75, + string $break = "\n", + bool $cut = false, + bool $add_final_break = true, + string $delimiter = null + ): string { + if ($delimiter === null) { + $strings = \preg_split('/\\r\\n|\\r|\\n/', $str); + } else { + $strings = \explode($delimiter, $str); + } + + $string_helper_array = []; + if ($strings !== false) { + foreach ($strings as $value) { + $string_helper_array[] = self::wordwrap($value, $width, $break, $cut); + } + } + + if ($add_final_break) { + $final_break = $break; + } else { + $final_break = ''; + } + + return \implode($delimiter ?? "\n", $string_helper_array) . $final_break; + } + + /** + * Returns an array of Unicode White Space characters. + * + * @psalm-pure + * + * @return string[] + *

An array with numeric code point as key and White Space Character as value.

+ */ + public static function ws(): array + { + return self::$WHITESPACE; + } + + /** + * Checks whether the passed string contains only byte sequences that are valid UTF-8 characters. + * + * EXAMPLE: + * UTF8::is_utf8_string('Iñtërnâtiônàlizætiøn']); // true + * // + * UTF8::is_utf8_string("Iñtërnâtiônàlizætiøn\xA0\xA1"); // false + * + * + * @see http://hsivonen.iki.fi/php-utf8/ + * + * @param string $str

The string to be checked.

+ * @param bool $strict

Check also if the string is not UTF-16 or UTF-32.

+ * + * @psalm-pure + * + * @return bool + */ + private static function is_utf8_string(string $str, bool $strict = false) + { + if ($str === '') { + return true; + } + + if ($strict) { + $is_binary = self::is_binary($str, true); + + if ($is_binary && self::is_utf16($str, false) !== false) { + return false; + } + + if ($is_binary && self::is_utf32($str, false) !== false) { + return false; + } + } + + if (self::$SUPPORT['pcre_utf8']) { + // If even just the first character can be matched, when the /u + // modifier is used, then it's valid UTF-8. If the UTF-8 is somehow + // invalid, nothing at all will match, even if the string contains + // some valid sequences + return \preg_match('/^./us', $str) === 1; + } + + $mState = 0; // cached expected number of octets after the current octet + // until the beginning of the next UTF8 character sequence + $mUcs4 = 0; // cached Unicode character + $mBytes = 1; // cached expected number of octets in the current sequence + + if (self::$ORD === null) { + self::$ORD = self::getData('ord'); + } + + $len = \strlen($str); + for ($i = 0; $i < $len; ++$i) { + $in = self::$ORD[$str[$i]]; + + if ($mState === 0) { + // When mState is zero we expect either a US-ASCII character or a + // multi-octet sequence. + if ((0x80 & $in) === 0) { + // US-ASCII, pass straight through. + $mBytes = 1; + } elseif ((0xE0 & $in) === 0xC0) { + // First octet of 2 octet sequence. + $mUcs4 = $in; + $mUcs4 = ($mUcs4 & 0x1F) << 6; + $mState = 1; + $mBytes = 2; + } elseif ((0xF0 & $in) === 0xE0) { + // First octet of 3 octet sequence. + $mUcs4 = $in; + $mUcs4 = ($mUcs4 & 0x0F) << 12; + $mState = 2; + $mBytes = 3; + } elseif ((0xF8 & $in) === 0xF0) { + // First octet of 4 octet sequence. + $mUcs4 = $in; + $mUcs4 = ($mUcs4 & 0x07) << 18; + $mState = 3; + $mBytes = 4; + } elseif ((0xFC & $in) === 0xF8) { + /* First octet of 5 octet sequence. + * + * This is illegal because the encoded codepoint must be either + * (a) not the shortest form or + * (b) outside the Unicode range of 0-0x10FFFF. + * Rather than trying to resynchronize, we will carry on until the end + * of the sequence and let the later error handling code catch it. + */ + $mUcs4 = $in; + $mUcs4 = ($mUcs4 & 0x03) << 24; + $mState = 4; + $mBytes = 5; + } elseif ((0xFE & $in) === 0xFC) { + // First octet of 6 octet sequence, see comments for 5 octet sequence. + $mUcs4 = $in; + $mUcs4 = ($mUcs4 & 1) << 30; + $mState = 5; + $mBytes = 6; + } else { + // Current octet is neither in the US-ASCII range nor a legal first + // octet of a multi-octet sequence. + return false; + } + } elseif ((0xC0 & $in) === 0x80) { + + // When mState is non-zero, we expect a continuation of the multi-octet + // sequence + + // Legal continuation. + $shift = ($mState - 1) * 6; + $tmp = $in; + $tmp = ($tmp & 0x0000003F) << $shift; + $mUcs4 |= $tmp; + // Prefix: End of the multi-octet sequence. mUcs4 now contains the final + // Unicode code point to be output. + if (--$mState === 0) { + // Check for illegal sequences and code points. + // + // From Unicode 3.1, non-shortest form is illegal + if ( + ($mBytes === 2 && $mUcs4 < 0x0080) + || + ($mBytes === 3 && $mUcs4 < 0x0800) + || + ($mBytes === 4 && $mUcs4 < 0x10000) + || + ($mBytes > 4) + || + // From Unicode 3.2, surrogate characters are illegal. + (($mUcs4 & 0xFFFFF800) === 0xD800) + || + // Code points outside the Unicode range are illegal. + ($mUcs4 > 0x10FFFF) + ) { + return false; + } + // initialize UTF8 cache + $mState = 0; + $mUcs4 = 0; + $mBytes = 1; + } + } else { + // ((0xC0 & (*in) != 0x80) && (mState != 0)) + // Incomplete multi-octet sequence. + return false; + } + } + + return $mState === 0; + } + + /** + * @param string $str + * @param bool $use_lowercase

Use uppercase by default, otherwise use lowercase.

+ * @param bool $use_full_case_fold

Convert not only common cases.

+ * + * @psalm-pure + * + * @return string + */ + private static function fixStrCaseHelper( + string $str, + bool $use_lowercase = false, + bool $use_full_case_fold = false + ) { + $upper = self::$COMMON_CASE_FOLD['upper']; + $lower = self::$COMMON_CASE_FOLD['lower']; + + if ($use_lowercase) { + $str = \str_replace( + $upper, + $lower, + $str + ); + } else { + $str = \str_replace( + $lower, + $upper, + $str + ); + } + + if ($use_full_case_fold) { + /** + * @psalm-suppress ImpureStaticVariable + * + * @var array|null + */ + static $FULL_CASE_FOLD = null; + if ($FULL_CASE_FOLD === null) { + $FULL_CASE_FOLD = self::getData('caseFolding_full'); + } + + if ($use_lowercase) { + $str = \str_replace($FULL_CASE_FOLD[0], $FULL_CASE_FOLD[1], $str); + } else { + $str = \str_replace($FULL_CASE_FOLD[1], $FULL_CASE_FOLD[0], $str); + } + } + + return $str; + } + + /** + * get data from "/data/*.php" + * + * @param string $file + * + * @psalm-pure + * + * @return array + */ + private static function getData(string $file) + { + /** @noinspection PhpIncludeInspection */ + /** @noinspection UsingInclusionReturnValueInspection */ + /** @psalm-suppress UnresolvableInclude */ + return include __DIR__ . '/data/' . $file . '.php'; + } + + /** + * @psalm-pure + * + * @return true|null + */ + private static function initEmojiData() + { + if (self::$EMOJI_KEYS_CACHE === null) { + if (self::$EMOJI === null) { + self::$EMOJI = self::getData('emoji'); + } + + /** + * @psalm-suppress ImpureFunctionCall - static sort function is used + */ + \uksort( + self::$EMOJI, + static function (string $a, string $b): int { + return \strlen($b) <=> \strlen($a); + } + ); + + self::$EMOJI_KEYS_CACHE = \array_keys(self::$EMOJI); + self::$EMOJI_VALUES_CACHE = self::$EMOJI; + + foreach (self::$EMOJI_KEYS_CACHE as $key) { + $tmp_key = \crc32($key); + self::$EMOJI_KEYS_REVERSIBLE_CACHE[] = '_-_PORTABLE_UTF8_-_' . $tmp_key . '_-_' . \strrev((string) $tmp_key) . '_-_8FTU_ELBATROP_-_'; + } + + return true; + } + + return null; + } + + /** + * Checks whether mbstring "overloaded" is active on the server. + * + * @psalm-pure + * + * @return bool + */ + private static function mbstring_overloaded(): bool + { + /** + * INI directive 'mbstring.func_overload' is deprecated since PHP 7.2 + */ + + /** @noinspection PhpComposerExtensionStubsInspection */ + /** @noinspection PhpUsageOfSilenceOperatorInspection */ + /** @noinspection DeprecatedIniOptionsInspection */ + return \defined('MB_OVERLOAD_STRING') + && + ((int) @\ini_get('mbstring.func_overload') & \MB_OVERLOAD_STRING); + } + + /** + * @param string[] $strings + * @param bool $remove_empty_values + * @param int|null $remove_short_values + * + * @psalm-pure + * + * @return list + */ + private static function reduce_string_array( + array $strings, + bool $remove_empty_values, + int $remove_short_values = null + ) { + // init + $return = []; + + foreach ($strings as &$str) { + if ( + $remove_short_values !== null + && + \mb_strlen($str) <= $remove_short_values + ) { + continue; + } + + if ( + $remove_empty_values + && + \trim($str) === '' + ) { + continue; + } + + $return[] = $str; + } + + return $return; + } + + /** + * rxClass + * + * @param string $s + * @param string $class + * + * @return string + * + * @psalm-pure + */ + private static function rxClass(string $s, string $class = '') + { + /** + * @psalm-suppress ImpureStaticVariable + * + * @var array + */ + static $RX_CLASS_CACHE = []; + + $cache_key = $s . '_' . $class; + + if (isset($RX_CLASS_CACHE[$cache_key])) { + return $RX_CLASS_CACHE[$cache_key]; + } + + $class_array = []; + $class_array[] = $class; + + /** @noinspection SuspiciousLoopInspection */ + /** @noinspection AlterInForeachInspection */ + foreach (self::str_split($s) as &$s) { + if ($s === '-') { + $class_array[0] = '-' . $class_array[0]; + } elseif (!isset($s[2])) { + $class_array[0] .= \preg_quote($s, '/'); + } elseif (self::strlen($s) === 1) { + $class_array[0] .= $s; + } else { + $class_array[] = $s; + } + } + + if ($class_array[0]) { + $class_array[0] = '[' . $class_array[0] . ']'; + } + + if (\count($class_array) === 1) { + $return = $class_array[0]; + } else { + $return = '(?:' . \implode('|', $class_array) . ')'; + } + + $RX_CLASS_CACHE[$cache_key] = $return; + + return $return; + } + + /** + * Personal names such as "Marcus Aurelius" are sometimes typed incorrectly using lowercase ("marcus aurelius"). + * + * @param string $names + * @param string $delimiter + * @param string $encoding + * + * @phpstan-param non-empty-string $delimiter + * + * @psalm-pure + * + * @return string + */ + private static function str_capitalize_name_helper( + string $names, + string $delimiter, + string $encoding = 'UTF-8' + ) { + // init + try { + $name_helper_array = \explode($delimiter, $names); + } catch (\Error $e) { + // PHP >= 8.0 : explode() will now throw ValueError when separator parameter is given an empty string (""). Previously, explode() returned false instead. + $name_helper_array = false; + } + if ($name_helper_array === false) { + return ''; + } + + $special_cases = [ + 'names' => [ + 'ab', + 'af', + 'al', + 'and', + 'ap', + 'bint', + 'binte', + 'da', + 'de', + 'del', + 'den', + 'der', + 'di', + 'dit', + 'ibn', + 'la', + 'mac', + 'nic', + 'of', + 'ter', + 'the', + 'und', + 'van', + 'von', + 'y', + 'zu', + ], + 'prefixes' => [ + 'al-', + "d'", + 'ff', + "l'", + 'mac', + 'mc', + 'nic', + ], + ]; + + foreach ($name_helper_array as &$name) { + if (\in_array($name, $special_cases['names'], true)) { + continue; + } + + $continue = false; + + if ($delimiter === '-') { + foreach ((array) $special_cases['names'] as &$beginning) { + if (\strncmp($name, $beginning, \strlen($beginning)) === 0) { + $continue = true; + + break; + } + } + unset($beginning); + } + + foreach ((array) $special_cases['prefixes'] as &$beginning) { + if (\strncmp($name, $beginning, \strlen($beginning)) === 0) { + $continue = true; + + break; + } + } + unset($beginning); + + if ($continue) { + continue; + } + + $name = self::ucfirst($name, $encoding); + } + + return \implode($delimiter, $name_helper_array); + } + + /** + * Generic case-sensitive transformation for collation matching. + * + * @param string $str

The input string

+ * + * @psalm-pure + * + * @return string|null + */ + private static function strtonatfold(string $str) + { + $str = \Normalizer::normalize($str, \Normalizer::NFD); + if ($str === false) { + return ''; + } + + return \preg_replace( + '/\p{Mn}+/u', + '', + $str + ); + } + + /** + * @param int|string $input + * + * @psalm-pure + * + * @return string + */ + private static function to_utf8_convert_helper($input) + { + // init + $buf = ''; + + if (self::$ORD === null) { + self::$ORD = self::getData('ord'); + } + + if (self::$CHR === null) { + self::$CHR = self::getData('chr'); + } + + if (self::$WIN1252_TO_UTF8 === null) { + self::$WIN1252_TO_UTF8 = self::getData('win1252_to_utf8'); + } + + $ordC1 = self::$ORD[$input]; + if (isset(self::$WIN1252_TO_UTF8[$ordC1])) { // found in Windows-1252 special cases + $buf .= self::$WIN1252_TO_UTF8[$ordC1]; + } else { + $cc1 = self::$CHR[$ordC1 / 64] | "\xC0"; + $cc2 = ((string) $input & "\x3F") | "\x80"; + $buf .= $cc1 . $cc2; + } + + return $buf; + } + + /** + * @param string $str + * + * @psalm-pure + * + * @return string + */ + private static function urldecode_unicode_helper(string $str) + { + if (\strpos($str, '%u') === false) { + return $str; + } + + $pattern = '/%u([0-9a-fA-F]{3,4})/'; + if (\preg_match($pattern, $str)) { + $str = (string) \preg_replace($pattern, '&#x\\1;', $str); + } + + return $str; + } +} \ No newline at end of file diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/ascii_by_languages.php b/includes/libraries/anti-xss-master/src/voku/helper/data/ascii_by_languages.php new file mode 100644 index 000000000..68c3f9d25 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/ascii_by_languages.php @@ -0,0 +1,2950 @@ + [ + 'Á' => 'A', + 'á' => 'a', + 'Ä' => 'A', + 'ä' => 'a', + 'À' => 'A', + 'à' => 'a', + 'Â' => 'A', + 'â' => 'a', + 'É' => 'E', + 'é' => 'e', + 'Ë' => 'E', + 'ë' => 'e', + 'È' => 'E', + 'è' => 'e', + 'Ê' => 'E', + 'ê' => 'e', + 'Í' => 'I', + 'í' => 'i', + 'Ï' => 'I', + 'ï' => 'i', + 'Ì' => 'I', + 'ì' => 'i', + 'Î' => 'I', + 'î' => 'i', + 'Ó' => 'O', + 'ó' => 'o', + 'Ö' => 'O', + 'ö' => 'o', + 'Ò' => 'O', + 'ò' => 'o', + 'Ô' => 'O', + 'ô' => 'o', + 'Ú' => 'U', + 'ú' => 'u', + 'Ü' => 'U', + 'ü' => 'u', + 'Ù' => 'U', + 'ù' => 'u', + 'Û' => 'U', + 'û' => 'u', + 'Ý' => 'Y', + 'ý' => 'y', + 'Ÿ' => 'Y', + ], + // Italian + 'it' => [ + 'à' => 'a', + 'À' => 'A', + 'é' => 'e', + 'É' => 'E', + 'è' => 'e', + 'È' => 'E', + 'ì' => 'i', + 'Ì' => 'I', + 'Ò' => 'O', + 'ò' => 'o', + 'ù' => 'u', + 'Ù' => 'U', + ], + // Macedonian + 'mk' => [ + 'А' => 'A', + 'Б' => 'B', + 'В' => 'V', + 'Г' => 'G', + 'Д' => 'D', + 'Ѓ' => 'Gj', + 'Е' => 'E', + 'Ж' => 'Zh', + 'З' => 'Z', + 'Ѕ' => 'Dz', + 'И' => 'I', + 'Ј' => 'J', + 'К' => 'K', + 'Л' => 'L', + 'Љ' => 'Lj', + 'М' => 'M', + 'Н' => 'N', + 'Њ' => 'Nj', + 'О' => 'O', + 'П' => 'P', + 'Р' => 'R', + 'С' => 'S', + 'Т' => 'T', + 'Ќ' => 'Kj', + 'У' => 'U', + 'Ф' => 'F', + 'Х' => 'H', + 'Ц' => 'C', + 'Ч' => 'Ch', + 'Џ' => 'Dj', + 'Ш' => 'Sh', + 'а' => 'a', + 'б' => 'b', + 'в' => 'v', + 'г' => 'g', + 'д' => 'd', + 'ѓ' => 'gj', + 'е' => 'e', + 'ж' => 'zh', + 'з' => 'z', + 'ѕ' => 'dz', + 'и' => 'i', + 'ј' => 'j', + 'к' => 'k', + 'л' => 'l', + 'љ' => 'lj', + 'м' => 'm', + 'н' => 'n', + 'њ' => 'nj', + 'о' => 'o', + 'п' => 'p', + 'р' => 'r', + 'с' => 's', + 'т' => 't', + 'ќ' => 'kj', + 'у' => 'u', + 'ф' => 'f', + 'х' => 'h', + 'ц' => 'c', + 'ч' => 'ch', + 'џ' => 'dj', + 'ш' => 'sh', + ], + // Portuguese (Brazil) + 'pt' => [ + 'æ' => 'ae', + 'ǽ' => 'ae', + 'À' => 'A', + 'Á' => 'A', + 'Â' => 'A', + 'Ã' => 'A', + 'Å' => 'AA', + 'Ǻ' => 'A', + 'Ă' => 'A', + 'Ǎ' => 'A', + 'Æ' => 'AE', + 'Ǽ' => 'AE', + 'à' => 'a', + 'á' => 'a', + 'â' => 'a', + 'ã' => 'a', + 'å' => 'aa', + 'ǻ' => 'a', + 'ă' => 'a', + 'ǎ' => 'a', + 'ª' => 'a', + 'Ĉ' => 'C', + 'Ċ' => 'C', + 'Ç' => 'C', + 'ç' => 'c', + 'ĉ' => 'c', + 'ċ' => 'c', + 'Ð' => 'Dj', + 'Đ' => 'D', + 'ð' => 'dj', + 'đ' => 'd', + 'È' => 'E', + 'É' => 'E', + 'Ê' => 'E', + 'Ë' => 'E', + 'Ĕ' => 'E', + 'Ė' => 'E', + 'è' => 'e', + 'é' => 'e', + 'ê' => 'e', + 'ë' => 'e', + 'ĕ' => 'e', + 'ė' => 'e', + 'ƒ' => 'f', + 'Ĝ' => 'G', + 'Ġ' => 'G', + 'ĝ' => 'g', + 'ġ' => 'g', + 'Ĥ' => 'H', + 'Ħ' => 'H', + 'ĥ' => 'h', + 'ħ' => 'h', + 'Ì' => 'I', + 'Í' => 'I', + 'Î' => 'I', + 'Ï' => 'I', + 'Ĩ' => 'I', + 'Ĭ' => 'I', + 'Ǐ' => 'I', + 'Į' => 'I', + 'IJ' => 'IJ', + 'ì' => 'i', + 'í' => 'i', + 'î' => 'i', + 'ï' => 'i', + 'ĩ' => 'i', + 'ĭ' => 'i', + 'ǐ' => 'i', + 'į' => 'i', + 'ij' => 'ij', + 'Ĵ' => 'J', + 'ĵ' => 'j', + 'Ĺ' => 'L', + 'Ľ' => 'L', + 'Ŀ' => 'L', + 'ĺ' => 'l', + 'ľ' => 'l', + 'ŀ' => 'l', + 'Ñ' => 'N', + 'ñ' => 'n', + 'ʼn' => 'n', + 'Ò' => 'O', + 'Ó' => 'O', + 'Ô' => 'O', + 'Õ' => 'O', + 'Ō' => 'O', + 'Ŏ' => 'O', + 'Ǒ' => 'O', + 'Ő' => 'O', + 'Ơ' => 'O', + 'Ø' => 'OE', + 'Ǿ' => 'O', + 'Œ' => 'OE', + 'ò' => 'o', + 'ó' => 'o', + 'ô' => 'o', + 'õ' => 'o', + 'ō' => 'o', + 'ŏ' => 'o', + 'ǒ' => 'o', + 'ő' => 'o', + 'ơ' => 'o', + 'ø' => 'oe', + 'ǿ' => 'o', + 'º' => 'o', + 'œ' => 'oe', + 'Ŕ' => 'R', + 'Ŗ' => 'R', + 'ŕ' => 'r', + 'ŗ' => 'r', + 'Ŝ' => 'S', + 'Ș' => 'S', + 'ŝ' => 's', + 'ș' => 's', + 'ſ' => 's', + 'Ţ' => 'T', + 'Ț' => 'T', + 'Ŧ' => 'T', + 'Þ' => 'TH', + 'ţ' => 't', + 'ț' => 't', + 'ŧ' => 't', + 'þ' => 'th', + 'Ù' => 'U', + 'Ú' => 'U', + 'Û' => 'U', + 'Ü' => 'U', + 'Ũ' => 'U', + 'Ŭ' => 'U', + 'Ű' => 'U', + 'Ų' => 'U', + 'Ư' => 'U', + 'Ǔ' => 'U', + 'Ǖ' => 'U', + 'Ǘ' => 'U', + 'Ǚ' => 'U', + 'Ǜ' => 'U', + 'ù' => 'u', + 'ú' => 'u', + 'û' => 'u', + 'ü' => 'u', + 'ũ' => 'u', + 'ŭ' => 'u', + 'ű' => 'u', + 'ų' => 'u', + 'ư' => 'u', + 'ǔ' => 'u', + 'ǖ' => 'u', + 'ǘ' => 'u', + 'ǚ' => 'u', + 'ǜ' => 'u', + 'Ŵ' => 'W', + 'ŵ' => 'w', + 'Ý' => 'Y', + 'Ÿ' => 'Y', + 'Ŷ' => 'Y', + 'ý' => 'y', + 'ÿ' => 'y', + 'ŷ' => 'y', + ], + // Greek(lish) (Elláda) + 'el__greeklish' => [ + 'ΑΥ' => 'AU', + 'ΑΎ' => 'AU', + 'Αυ' => 'Au', + 'Αύ' => 'Au', + 'ΕΊ' => 'EI', + 'ΕΙ' => 'EI', + 'Ει' => 'EI', + 'ΕΥ' => 'EU', + 'ΕΎ' => 'EU', + 'Εί' => 'Ei', + 'Ευ' => 'Eu', + 'Εύ' => 'Eu', + 'ΟΙ' => 'OI', + 'ΟΊ' => 'OI', + 'ΟΥ' => 'OU', + 'ΟΎ' => 'OU', + 'Οι' => 'Oi', + 'Οί' => 'Oi', + 'Ου' => 'Ou', + 'Ού' => 'Ou', + 'ΥΙ' => 'YI', + 'ΎΙ' => 'YI', + 'Υι' => 'Yi', + 'Ύι' => 'Yi', + 'ΥΊ' => 'Yi', + 'Υί' => 'Yi', + 'αυ' => 'au', + 'αύ' => 'au', + 'εί' => 'ei', + 'ει' => 'ei', + 'ευ' => 'eu', + 'εύ' => 'eu', + 'οι' => 'oi', + 'οί' => 'oi', + 'ου' => 'ou', + 'ού' => 'ou', + 'υι' => 'yi', + 'ύι' => 'yi', + 'υί' => 'yi', + 'Α' => 'A', + 'Ά' => 'A', + 'Β' => 'B', + 'Δ' => 'D', + 'Ε' => 'E', + 'Έ' => 'E', + 'Φ' => 'F', + 'Γ' => 'G', + 'Η' => 'H', + 'Ή' => 'H', + 'Ι' => 'I', + 'Ί' => 'I', + 'Ϊ' => 'I', + 'Κ' => 'K', + 'Ξ' => 'Ks', + 'Λ' => 'L', + 'Μ' => 'M', + 'Ν' => 'N', + 'Π' => 'N', + 'Ο' => 'O', + 'Ό' => 'O', + 'Ψ' => 'Ps', + 'Ρ' => 'R', + 'Σ' => 'S', + 'Τ' => 'T', + 'Θ' => 'Th', + 'Ω' => 'W', + 'Ώ' => 'W', + 'Χ' => 'X', + 'ϒ' => 'Y', + 'Υ' => 'Y', + 'Ύ' => 'Y', + 'Ϋ' => 'Y', + 'Ζ' => 'Z', + 'α' => 'a', + 'ά' => 'a', + 'β' => 'b', + 'δ' => 'd', + 'ε' => 'e', + 'έ' => 'e', + 'φ' => 'f', + 'γ' => 'g', + 'η' => 'h', + 'ή' => 'h', + 'ι' => 'i', + 'ί' => 'i', + 'ϊ' => 'i', + 'ΐ' => 'i', + 'κ' => 'k', + 'ξ' => 'ks', + 'λ' => 'l', + 'μ' => 'm', + 'ν' => 'n', + 'ο' => 'o', + 'ό' => 'o', + 'π' => 'p', + 'ψ' => 'ps', + 'ρ' => 'r', + 'σ' => 's', + 'ς' => 's', + 'τ' => 't', + 'ϑ' => 'th', + 'θ' => 'th', + 'ϐ' => 'v', + 'ω' => 'w', + 'ώ' => 'w', + 'χ' => 'x', + 'υ' => 'y', + 'ύ' => 'y', + 'ΰ' => 'y', + 'ϋ' => 'y', + 'ζ' => 'z', + ], + // Greek (Elláda) + 'el' => [ + 'ΑΥ' => 'AU', + 'Αυ' => 'Au', + 'ΟΥ' => 'U', + 'Ου' => 'u', + 'ΕΥ' => 'EF', + 'Ευ' => 'Ef', + 'ΕΙ' => 'I', + 'Ει' => 'I', + 'ΟΙ' => 'I', + 'Οι' => 'I', + 'ΥΙ' => 'I', + 'Υι' => 'I', + 'ΑΎ' => 'AU', + 'Αύ' => 'Au', + 'ΟΎ' => 'OU', + 'Ού' => 'Ou', + 'ΕΎ' => 'EU', + 'Εύ' => 'Eu', + 'ΕΊ' => 'I', + 'Εί' => 'I', + 'ΟΊ' => 'I', + 'Οί' => 'I', + 'ΎΙ' => 'I', + 'Ύι' => 'I', + 'ΥΊ' => 'I', + 'Υί' => 'I', + 'αυ' => 'au', + 'ου' => 'u', + 'ευ' => 'ef', + 'ει' => 'i', + 'οι' => 'i', + 'υι' => 'i', + 'αύ' => 'au', + 'ού' => 'ou', + 'εύ' => 'eu', + 'εί' => 'i', + 'οί' => 'i', + 'ύι' => 'i', + 'υί' => 'i', + 'α' => 'a', + 'β' => 'v', + 'γ' => 'gh', + 'δ' => 'd', + 'ε' => 'e', + 'ζ' => 'z', + 'η' => 'i', + 'θ' => 'th', + 'ι' => 'i', + 'κ' => 'k', + 'λ' => 'l', + 'μ' => 'm', + 'ν' => 'n', + 'ξ' => 'ks', + 'ο' => 'o', + 'π' => 'p', + 'ρ' => 'r', + 'σ' => 's', + 'τ' => 't', + 'υ' => 'i', + 'φ' => 'f', + 'χ' => 'kh', + 'ψ' => 'ps', + 'ω' => 'o', + 'ά' => 'a', + 'έ' => 'e', + 'ί' => 'i', + 'ό' => 'o', + 'ϒ' => 'Y', + 'ύ' => 'y', + 'ή' => 'i', + 'ώ' => 'w', + 'ς' => 's', + 'ϊ' => 'i', + 'ΰ' => 'y', + 'ϋ' => 'y', + 'ΐ' => 'i', + 'Α' => 'A', + 'Β' => 'B', + 'Γ' => 'G', + 'Δ' => 'D', + 'Ε' => 'E', + 'Ζ' => 'Z', + 'Η' => 'H', + 'Θ' => 'Th', + 'Ι' => 'I', + 'Κ' => 'K', + 'Λ' => 'L', + 'Μ' => 'M', + 'Ν' => 'N', + 'Ξ' => 'Ks', + 'Ο' => 'O', + 'Π' => 'P', + 'Ρ' => 'R', + 'Σ' => 'S', + 'Τ' => 'T', + 'Υ' => 'Y', + 'Φ' => 'F', + 'Χ' => 'X', + 'Ψ' => 'Ps', + 'Ω' => 'O', + 'Ά' => 'A', + 'Έ' => 'E', + 'Ί' => 'I', + 'Ό' => 'O', + 'Ύ' => 'Y', + 'Ή' => 'I', + 'Ώ' => 'W', + 'Ϊ' => 'I', + 'Ϋ' => 'Y', + 'ϐ' => 'v', + 'ϑ' => 'th', + ], + // Hindi + 'hi' => [ + 'अ' => 'a', + 'आ' => 'aa', + 'ए' => 'e', + 'ई' => 'ii', + 'ऍ' => 'ei', + 'ऎ' => 'ae', + 'ऐ' => 'ai', + 'इ' => 'i', + 'ओ' => 'o', + 'ऑ' => 'oi', + 'ऒ' => 'oii', + 'ऊ' => 'uu', + 'औ' => 'ou', + 'उ' => 'u', + 'ब' => 'B', + 'भ' => 'Bha', + 'च' => 'Ca', + 'छ' => 'Chha', + 'ड' => 'Da', + 'ढ' => 'Dha', + 'फ' => 'Fa', + 'फ़' => 'Fi', + 'ग' => 'Ga', + 'घ' => 'Gha', + 'ग़' => 'Ghi', + 'ह' => 'Ha', + 'ज' => 'Ja', + 'झ' => 'Jha', + 'क' => 'Ka', + 'ख' => 'Kha', + 'ख़' => 'Khi', + 'ल' => 'L', + 'ळ' => 'Li', + 'ऌ' => 'Li', + 'ऴ' => 'Lii', + 'ॡ' => 'Lii', + 'म' => 'Ma', + 'न' => 'Na', + 'ङ' => 'Na', + 'ञ' => 'Nia', + 'ण' => 'Nae', + 'ऩ' => 'Ni', + 'ॐ' => 'oms', + 'प' => 'Pa', + 'क़' => 'Qi', + 'र' => 'Ra', + 'ऋ' => 'Ri', + 'ॠ' => 'Ri', + 'ऱ' => 'Ri', + 'स' => 'Sa', + 'श' => 'Sha', + 'ष' => 'Shha', + 'ट' => 'Ta', + 'त' => 'Ta', + 'ठ' => 'Tha', + 'द' => 'Tha', + 'थ' => 'Tha', + 'ध' => 'Thha', + 'ड़' => 'ugDha', + 'ढ़' => 'ugDhha', + 'व' => 'Va', + 'य' => 'Ya', + 'य़' => 'Yi', + 'ज़' => 'Za', + ], + // Armenian + 'hy' => [ + 'Ա' => 'A', + 'Բ' => 'B', + 'Գ' => 'G', + 'Դ' => 'D', + 'Ե' => 'E', + 'Զ' => 'Z', + 'Է' => 'E', + 'Ը' => 'Y', + 'Թ' => 'Th', + 'Ժ' => 'Zh', + 'Ի' => 'I', + 'Լ' => 'L', + 'Խ' => 'Kh', + 'Ծ' => 'Ts', + 'Կ' => 'K', + 'Հ' => 'H', + 'Ձ' => 'Dz', + 'Ղ' => 'Gh', + 'Ճ' => 'Tch', + 'Մ' => 'M', + 'Յ' => 'Y', + 'Ն' => 'N', + 'Շ' => 'Sh', + 'Ո' => 'Vo', + 'Չ' => 'Ch', + 'Պ' => 'P', + 'Ջ' => 'J', + 'Ռ' => 'R', + 'Ս' => 'S', + 'Վ' => 'V', + 'Տ' => 'T', + 'Ր' => 'R', + 'Ց' => 'C', + 'Ւ' => 'u', + 'Փ' => 'Ph', + 'Ք' => 'Q', + 'և' => 'ev', + 'Օ' => 'O', + 'Ֆ' => 'F', + 'ա' => 'a', + 'բ' => 'b', + 'գ' => 'g', + 'դ' => 'd', + 'ե' => 'e', + 'զ' => 'z', + 'է' => 'e', + 'ը' => 'y', + 'թ' => 'th', + 'ժ' => 'zh', + 'ի' => 'i', + 'լ' => 'l', + 'խ' => 'kh', + 'ծ' => 'ts', + 'կ' => 'k', + 'հ' => 'h', + 'ձ' => 'dz', + 'ղ' => 'gh', + 'ճ' => 'tch', + 'մ' => 'm', + 'յ' => 'y', + 'ն' => 'n', + 'շ' => 'sh', + 'ո' => 'vo', + 'չ' => 'ch', + 'պ' => 'p', + 'ջ' => 'j', + 'ռ' => 'r', + 'ս' => 's', + 'վ' => 'v', + 'տ' => 't', + 'ր' => 'r', + 'ց' => 'c', + 'ւ' => 'u', + 'փ' => 'ph', + 'ք' => 'q', + 'օ' => 'o', + 'ֆ' => 'f', + ], + // Swedish + 'sv' => [ + 'Ä' => 'A', + 'ä' => 'a', + 'Å' => 'A', + 'å' => 'a', + 'Ö' => 'O', + 'ö' => 'o', + ], + // Turkmen + 'tk' => [ + 'Ç' => 'C', + 'Ä' => 'A', + 'Ž' => 'Z', + 'Ň' => 'N', + 'Ö' => 'O', + 'Ş' => 'S', + 'Ü' => 'U', + 'Ý' => 'Y', + 'ç' => 'c', + 'ä' => 'a', + 'ž' => 'z', + 'ň' => 'n', + 'ö' => 'o', + 'ş' => 's', + 'ü' => 'u', + 'ý' => 'y', + ], + // Turkish + 'tr' => [ + 'ň' => 'n', + 'Ň' => 'N', + 'ş' => 's', + 'Ş' => 'S', + 'ı' => 'i', + 'İ' => 'I', + 'ç' => 'c', + 'Ç' => 'C', + 'ä' => 'a', + 'Ä' => 'A', + 'ü' => 'u', + 'Ü' => 'U', + 'ö' => 'o', + 'Ö' => 'O', + 'ğ' => 'g', + 'Ğ' => 'G', + 'ý' => 'y', + 'Ý' => 'Y', + 'ž' => 'z', + 'Ž' => 'Z', + ], + // Bulgarian + 'bg' => [ + 'ьо' => 'yo', + 'А' => 'A', + 'Б' => 'B', + 'В' => 'V', + 'Г' => 'G', + 'Д' => 'D', + 'Е' => 'E', + 'Ж' => 'Zh', + 'З' => 'Z', + 'И' => 'I', + 'Й' => 'Y', + 'К' => 'K', + 'Л' => 'L', + 'М' => 'M', + 'Н' => 'N', + 'О' => 'O', + 'П' => 'P', + 'Р' => 'R', + 'С' => 'S', + 'Т' => 'T', + 'У' => 'U', + 'Ф' => 'F', + 'Х' => 'H', + 'Ц' => 'C', + 'Ч' => 'Ch', + 'Ш' => 'Sh', + 'Щ' => 'Sht', + 'Ъ' => 'A', + 'Ь' => '', + 'Ю' => 'Yu', + 'Я' => 'Ya', + 'а' => 'a', + 'б' => 'b', + 'в' => 'v', + 'г' => 'g', + 'д' => 'd', + 'е' => 'e', + 'ж' => 'zh', + 'з' => 'z', + 'и' => 'i', + 'й' => 'y', + 'к' => 'k', + 'л' => 'l', + 'м' => 'm', + 'н' => 'n', + 'о' => 'o', + 'п' => 'p', + 'р' => 'r', + 'с' => 's', + 'т' => 't', + 'у' => 'u', + 'ф' => 'f', + 'х' => 'h', + 'ц' => 'c', + 'ч' => 'ch', + 'ш' => 'sh', + 'щ' => 'sht', + 'ъ' => 'a', + 'ь' => '', + 'ю' => 'yu', + 'я' => 'ya', + ], + // Hungarian + 'hu' => [ + 'Á' => 'A', + 'Ē' => 'E', + 'É' => 'E', + 'Í' => 'I', + 'Ó' => 'O', + 'Ö' => 'O', + 'Ő' => 'O', + 'Ú' => 'U', + 'Ü' => 'U', + 'Ű' => 'U', + 'á' => 'a', + 'ē' => 'e', + 'é' => 'e', + 'í' => 'i', + 'ó' => 'o', + 'ö' => 'o', + 'ő' => 'o', + 'ú' => 'u', + 'ü' => 'u', + 'ű' => 'u', + ], + // Myanmar (Burmese) + 'my' => [ + 'န်ုပ်' => 'nub', + 'ောင်' => 'aung', + 'ိုက်' => 'aik', + 'ိုဒ်' => 'ok', + 'ိုင်' => 'aing', + 'ိုလ်' => 'ol', + 'ေါင်' => 'aung', + 'သြော' => 'aw', + 'ောက်' => 'auk', + 'ိတ်' => 'eik', + 'ုတ်' => 'ok', + 'ုန်' => 'on', + 'ေတ်' => 'it', + 'ုဒ်' => 'ait', + 'ာန်' => 'an', + 'ိန်' => 'ein', + 'ွတ်' => 'ut', + 'ေါ်' => 'aw', + 'ွန်' => 'un', + 'ိပ်' => 'eik', + 'ုပ်' => 'ok', + 'ွပ်' => 'ut', + 'ိမ်' => 'ein', + 'ုမ်' => 'on', + 'ော်' => 'aw', + 'ွမ်' => 'un', + 'က်' => 'et', + 'ေါ' => 'aw', + 'ော' => 'aw', + 'ျွ' => 'ywa', + 'ြွ' => 'yw', + 'ို' => 'o', + 'ုံ' => 'on', + 'တ်' => 'at', + 'င်' => 'in', + 'ည်' => 'i', + 'ဒ်' => 'd', + 'န်' => 'an', + 'ပ်' => 'at', + 'မ်' => 'an', + 'စျ' => 'za', + 'ယ်' => 'e', + 'ဉ်' => 'in', + 'စ်' => 'it', + 'ိံ' => 'ein', + 'ဲ' => 'e', + 'း' => '', + 'ာ' => 'a', + 'ါ' => 'a', + 'ေ' => 'e', + 'ံ' => 'an', + 'ိ' => 'i', + 'ီ' => 'i', + 'ု' => 'u', + 'ူ' => 'u', + '်' => 'at', + '္' => '', + '့' => '', + 'က' => 'k', + '၉' => '9', + 'တ' => 't', + 'ရ' => 'ya', + 'ယ' => 'y', + 'မ' => 'm', + 'ဘ' => 'ba', + 'ဗ' => 'b', + 'ဖ' => 'pa', + 'ပ' => 'p', + 'န' => 'n', + 'ဓ' => 'da', + 'ဒ' => 'd', + 'ထ' => 'ta', + 'ဏ' => 'na', + 'ဝ' => 'w', + 'ဎ' => 'da', + 'ဍ' => 'd', + 'ဌ' => 'ta', + 'ဋ' => 't', + 'ည' => 'ny', + 'ဇ' => 'z', + 'ဆ' => 'sa', + 'စ' => 's', + 'င' => 'ng', + 'ဃ' => 'ga', + 'ဂ' => 'g', + 'လ' => 'l', + 'သ' => 'th', + '၈' => '8', + 'ဩ' => 'aw', + 'ခ' => 'kh', + '၆' => '6', + '၅' => '5', + '၄' => '4', + '၃' => '3', + '၂' => '2', + '၁' => '1', + '၀' => '0', + '၌' => 'hnaik', + '၍' => 'ywae', + 'ဪ' => 'aw', + 'ဦ' => '-u', + 'ဟ' => 'h', + 'ဉ' => 'u', + 'ဤ' => '-i', + 'ဣ' => 'i', + '၏' => '-e', + 'ဧ' => 'e', + 'ှ' => 'h', + 'ွ' => 'w', + 'ျ' => 'ya', + 'ြ' => 'y', + 'အ' => 'a', + 'ဠ' => 'la', + '၇' => '7', + ], + // Croatian (Hrvatska) + 'hr' => [ + 'DŽ' => 'DZ', + 'Dž' => 'Dz', + 'dž' => 'dz', + 'DZ' => 'DZ', + 'Dz' => 'Dz', + 'dz' => 'dz', + 'IJ' => 'IJ', + 'ij' => 'ij', + 'LJ' => 'LJ', + 'Lj' => 'Lj', + 'lj' => 'lj', + 'NJ' => 'NJ', + 'Nj' => 'Nj', + 'nj' => 'nj', + 'ž' => 'z', + 'Ž' => 'Z', + 'đ' => 'dj', + 'Đ' => 'Dj', + 'č' => 'c', + 'Č' => 'C', + 'ć' => 'c', + 'Ć' => 'C', + 'š' => 's', + 'Š' => 'S', + ], + // Finnish + 'fi' => [ + 'Ä' => 'A', + 'Ö' => 'O', + 'ä' => 'a', + 'ö' => 'o', + ], + // Georgian (Kartvelian) + 'ka' => [ + 'ა' => 'a', + 'ბ' => 'b', + 'გ' => 'g', + 'დ' => 'd', + 'ე' => 'e', + 'ვ' => 'v', + 'ზ' => 'z', + 'თ' => 't', + 'ი' => 'i', + 'კ' => 'k', + 'ლ' => 'l', + 'მ' => 'm', + 'ნ' => 'n', + 'ო' => 'o', + 'პ' => 'p', + 'ჟ' => 'zh', + 'რ' => 'r', + 'ს' => 's', + 'ტ' => 't', + 'უ' => 'u', + 'ფ' => 'f', + 'ქ' => 'q', + 'ღ' => 'gh', + 'ყ' => 'y', + 'შ' => 'sh', + 'ჩ' => 'ch', + 'ც' => 'ts', + 'ძ' => 'dz', + 'წ' => 'ts', + 'ჭ' => 'ch', + 'ხ' => 'kh', + 'ჯ' => 'j', + 'ჰ' => 'h', + ], + // Russian + 'ru' => [ + 'А' => 'A', + 'а' => 'a', + 'Б' => 'B', + 'б' => 'b', + 'В' => 'V', + 'в' => 'v', + 'Г' => 'G', + 'г' => 'g', + 'Д' => 'D', + 'д' => 'd', + 'Е' => 'E', + 'е' => 'e', + 'Ё' => 'Yo', + 'ё' => 'yo', + 'Ж' => 'Zh', + 'ж' => 'zh', + 'З' => 'Z', + 'з' => 'z', + 'И' => 'I', + 'и' => 'i', + 'Й' => 'Y', + 'й' => 'y', + 'К' => 'K', + 'к' => 'k', + 'Л' => 'L', + 'л' => 'l', + 'М' => 'M', + 'м' => 'm', + 'Н' => 'N', + 'н' => 'n', + 'О' => 'O', + 'о' => 'o', + 'П' => 'P', + 'п' => 'p', + 'Р' => 'R', + 'р' => 'r', + 'С' => 'S', + 'с' => 's', + 'Т' => 'T', + 'т' => 't', + 'У' => 'U', + 'у' => 'u', + 'Ф' => 'F', + 'ф' => 'f', + 'Х' => 'H', + 'х' => 'h', + 'Ц' => 'Ts', + 'ц' => 'ts', + 'Ч' => 'Ch', + 'ч' => 'ch', + 'ш' => 'sh', + 'Ш' => 'Sh', + 'Щ' => 'Sch', + 'щ' => 'sch', + 'Ъ' => '', + 'ъ' => '', + 'Ы' => 'Y', + 'ы' => 'y', + 'Ь' => '', + 'ь' => '', + 'Э' => 'E', + 'э' => 'e', + 'Ю' => 'Yu', + 'ю' => 'yu', + 'Я' => 'Ya', + 'я' => 'ya', + ], + // Russian - GOST 7.79-2000(B) + // -> https://en.m.wikipedia.org/wiki/Romanization_of_Russian#content-collapsible-block-1 + 'ru__gost_2000_b' => [ + 'А' => 'A', + 'а' => 'a', + 'Б' => 'B', + 'б' => 'b', + 'В' => 'V', + 'в' => 'v', + 'Г' => 'G', + 'г' => 'g', + 'Д' => 'D', + 'д' => 'd', + 'Е' => 'E', + 'е' => 'e', + 'Ё' => 'Yo', + 'ё' => 'yo', + 'Ж' => 'Zh', + 'ж' => 'zh', + 'З' => 'Z', + 'з' => 'z', + 'И' => 'i', + 'и' => 'i', + 'Й' => 'i', + 'й' => 'i', + 'К' => 'K', + 'к' => 'k', + 'Л' => 'L', + 'л' => 'l', + 'М' => 'M', + 'м' => 'm', + 'Н' => 'N', + 'н' => 'n', + 'О' => 'O', + 'о' => 'o', + 'П' => 'P', + 'п' => 'p', + 'Р' => 'R', + 'р' => 'r', + 'С' => 'S', + 'с' => 's', + 'Т' => 'T', + 'т' => 't', + 'У' => 'U', + 'у' => 'u', + 'Ф' => 'F', + 'ф' => 'f', + 'Х' => 'X', + 'х' => 'x', + 'Ц' => 'Cz', + 'ц' => 'cz', + 'Ч' => 'Ch', + 'ч' => 'ch', + 'ш' => 'sh', + 'Ш' => 'Sh', + 'Щ' => 'Shh', + 'щ' => 'shh', + 'Ъ' => '', + 'ъ' => '', + 'Ы' => 'Y\'', + 'ы' => 'y\'', + 'Ь' => '', + 'ь' => '', + 'Э' => 'E\'', + 'э' => 'e\'', + 'Ю' => 'Yu', + 'ю' => 'yu', + 'Я' => 'Ya', + 'я' => 'ya', + 'І' => 'I', + 'і' => 'i', + 'Ѳ' => 'Fh', + 'ѳ' => 'fh', + 'Ѣ' => 'Ye', + 'ѣ' => 'ye', + 'Ѵ' => 'Yh', + 'ѵ' => 'yh', + 'Є' => '', + 'є' => '', + 'Ѥ' => '', + 'ѥ' => '', + 'Ѕ' => 'Js', + 'ѕ' => 'js', + 'Ꙋ' => '', + 'ꙋ' => '', + 'Ѡ' => '', + 'ѡ' => '', + 'Ѿ' => '', + 'ѿ' => '', + 'Ѫ' => '', + 'ѫ' => '', + 'Ѧ' => '', + 'ѧ' => '', + 'Ѭ' => '', + 'ѭ' => '', + 'Ѩ' => '', + 'ѩ' => '', + 'Ѯ' => '', + 'ѯ' => '', + 'Ѱ' => '', + 'ѱ' => '', + ], + // Russian - Passport (2013), ICAO + // -> https://en.m.wikipedia.org/wiki/Romanization_of_Russian#content-collapsible-block-1 + 'ru__passport_2013' => [ + 'А' => 'A', + 'а' => 'a', + 'Б' => 'B', + 'б' => 'b', + 'В' => 'V', + 'в' => 'v', + 'Г' => 'G', + 'г' => 'g', + 'Д' => 'D', + 'д' => 'd', + 'Е' => 'E', + 'е' => 'e', + 'Ё' => 'E', + 'ё' => 'e', + 'Ж' => 'Zh', + 'ж' => 'zh', + 'З' => 'Z', + 'з' => 'z', + 'И' => 'i', + 'и' => 'i', + 'Й' => 'i', + 'й' => 'i', + 'К' => 'K', + 'к' => 'k', + 'Л' => 'L', + 'л' => 'l', + 'М' => 'M', + 'м' => 'm', + 'Н' => 'N', + 'н' => 'n', + 'О' => 'O', + 'о' => 'o', + 'П' => 'P', + 'п' => 'p', + 'Р' => 'R', + 'р' => 'r', + 'С' => 'S', + 'с' => 's', + 'Т' => 'T', + 'т' => 't', + 'У' => 'U', + 'у' => 'u', + 'Ф' => 'F', + 'ф' => 'f', + 'Х' => 'Kh', + 'х' => 'kh', + 'Ц' => 'Ts', + 'ц' => 'ts', + 'Ч' => 'Ch', + 'ч' => 'ch', + 'ш' => 'sh', + 'Ш' => 'Sh', + 'Щ' => 'Shch', + 'щ' => 'shch', + 'Ъ' => 'Ie', + 'ъ' => 'ie', + 'Ы' => 'Y', + 'ы' => 'y', + 'Ь' => '', + 'ь' => '', + 'Э' => 'E', + 'э' => 'e', + 'Ю' => 'Iu', + 'ю' => 'iu', + 'Я' => 'Ia', + 'я' => 'ia', + 'І' => '', + 'і' => '', + 'Ѳ' => '', + 'ѳ' => '', + 'Ѣ' => '', + 'ѣ' => '', + 'Ѵ' => '', + 'ѵ' => '', + 'Є' => '', + 'є' => '', + 'Ѥ' => '', + 'ѥ' => '', + 'Ѕ' => '', + 'ѕ' => '', + 'Ꙋ' => '', + 'ꙋ' => '', + 'Ѡ' => '', + 'ѡ' => '', + 'Ѿ' => '', + 'ѿ' => '', + 'Ѫ' => '', + 'ѫ' => '', + 'Ѧ' => '', + 'ѧ' => '', + 'Ѭ' => '', + 'ѭ' => '', + 'Ѩ' => '', + 'ѩ' => '', + 'Ѯ' => '', + 'ѯ' => '', + 'Ѱ' => '', + 'ѱ' => '', + ], + // Ukrainian + // -> https://zakon.rada.gov.ua/laws/show/55-2010-%D0%BF?lang=en + 'uk' => [ + 'Г' => 'H', + 'г' => 'h', + 'Ґ' => 'G', + 'ґ' => 'g', + 'Є' => 'Ye', + 'є' => 'ye', + 'И' => 'Y', + 'и' => 'y', + 'І' => 'I', + 'і' => 'i', + 'Ї' => 'Yi', + 'ї' => 'yi', + 'Й' => 'Y', + 'й' => 'y', + 'Х' => 'Kh', + 'х' => 'kh', + 'Ц' => 'Ts', + 'ц' => 'ts', + 'Ч' => 'Ch', + 'ч' => 'ch', + 'Ш' => 'Sh', + 'ш' => 'sh', + 'Щ' => 'Shch', + 'щ' => 'shch', + ], + // Kazakh + 'kk' => [ + 'Ә' => 'A', + 'Ғ' => 'G', + 'Қ' => 'Q', + 'Ң' => 'N', + 'Ө' => 'O', + 'Ұ' => 'U', + 'Ү' => 'U', + 'Һ' => 'H', + 'ә' => 'a', + 'ғ' => 'g', + 'қ' => 'q', + 'ң' => 'n', + 'ө' => 'o', + 'ұ' => 'u', + 'ү' => 'u', + 'һ' => 'h', + ], + // Czech + 'cs' => [ + 'á' => 'a', + 'Á' => 'A', + 'č' => 'c', + 'Č' => 'C', + 'ď' => 'd', + 'Ď' => 'D', + 'é' => 'e', + 'É' => 'E', + 'ě' => 'e', + 'Ě' => 'E', + 'í' => 'i', + 'Í' => 'I', + 'ň' => 'n', + 'Ň' => 'N', + 'ó' => 'o', + 'Ó' => 'O', + 'ř' => 'r', + 'Ř' => 'R', + 'š' => 's', + 'Š' => 'S', + 'ť' => 't', + 'Ť' => 'T', + 'ú' => 'u', + 'Ú' => 'U', + 'ů' => 'u', + 'Ů' => 'U', + 'ý' => 'y', + 'Ý' => 'Y', + 'ž' => 'z', + 'Ž' => 'Z', + ], + // Danish + 'da' => [ + 'Æ' => 'Ae', + 'æ' => 'ae', + 'Ø' => 'Oe', + 'ø' => 'oe', + 'Å' => 'Aa', + 'å' => 'aa', + 'É' => 'E', + 'é' => 'e', + ], + // Polish + 'pl' => [ + 'ą' => 'a', + 'ć' => 'c', + 'ę' => 'e', + 'ł' => 'l', + 'ń' => 'n', + 'ó' => 'o', + 'ś' => 's', + 'ź' => 'z', + 'ż' => 'z', + 'Ą' => 'A', + 'Ć' => 'C', + 'Ę' => 'E', + 'Ł' => 'L', + 'Ń' => 'N', + 'Ó' => 'O', + 'Ś' => 'S', + 'Ź' => 'Z', + 'Ż' => 'Z', + ], + // Romanian + 'ro' => [ + 'ă' => 'a', + 'â' => 'a', + 'Ă' => 'A', + 'Â' => 'A', + 'î' => 'i', + 'Î' => 'I', + 'ș' => 's', + 'ş' => 's', + 'Ş' => 'S', + 'Ș' => 'S', + 'ț' => 't', + 'ţ' => 't', + 'Ţ' => 'T', + 'Ț' => 'T', + ], + // Esperanto + 'eo' => [ + 'ĉ' => 'cx', + 'ĝ' => 'gx', + 'ĥ' => 'hx', + 'ĵ' => 'jx', + 'ŝ' => 'sx', + 'ŭ' => 'ux', + 'Ĉ' => 'CX', + 'Ĝ' => 'GX', + 'Ĥ' => 'HX', + 'Ĵ' => 'JX', + 'Ŝ' => 'SX', + 'Ŭ' => 'UX', + ], + // Estonian + 'et' => [ + 'Š' => 'S', + 'Ž' => 'Z', + 'Õ' => 'O', + 'Ä' => 'A', + 'Ö' => 'O', + 'Ü' => 'U', + 'š' => 's', + 'ž' => 'z', + 'õ' => 'o', + 'ä' => 'a', + 'ö' => 'o', + 'ü' => 'u', + ], + // Latvian + 'lv' => [ + 'ā' => 'a', + 'č' => 'c', + 'ē' => 'e', + 'ģ' => 'g', + 'ī' => 'i', + 'ķ' => 'k', + 'ļ' => 'l', + 'ņ' => 'n', + 'š' => 's', + 'ū' => 'u', + 'ž' => 'z', + 'Ā' => 'A', + 'Č' => 'C', + 'Ē' => 'E', + 'Ģ' => 'G', + 'Ī' => 'i', + 'Ķ' => 'k', + 'Ļ' => 'L', + 'Ņ' => 'N', + 'Š' => 'S', + 'Ū' => 'u', + 'Ž' => 'Z', + ], + // Lithuanian + 'lt' => [ + 'ą' => 'a', + 'č' => 'c', + 'ę' => 'e', + 'ė' => 'e', + 'į' => 'i', + 'š' => 's', + 'ų' => 'u', + 'ū' => 'u', + 'ž' => 'z', + 'Ą' => 'A', + 'Č' => 'C', + 'Ę' => 'E', + 'Ė' => 'E', + 'Į' => 'I', + 'Š' => 'S', + 'Ų' => 'U', + 'Ū' => 'U', + 'Ž' => 'Z', + ], + // Norwegian + 'no' => [ + 'Æ' => 'AE', + 'æ' => 'ae', + 'Ø' => 'OE', + 'ø' => 'oe', + 'Å' => 'AA', + 'å' => 'aa', + ], + // Vietnamese + 'vi' => [ + 'Á' => 'A', + 'À' => 'A', + 'Ả' => 'A', + 'Ã' => 'A', + 'Ạ' => 'A', + 'Ă' => 'A', + 'Ắ' => 'A', + 'Ằ' => 'A', + 'Ẳ' => 'A', + 'Ẵ' => 'A', + 'Ặ' => 'A', + 'Â' => 'A', + 'Ấ' => 'A', + 'Ầ' => 'A', + 'Ẩ' => 'A', + 'Ẫ' => 'A', + 'Ậ' => 'A', + 'á' => 'a', + 'à' => 'a', + 'ả' => 'a', + 'ã' => 'a', + 'ạ' => 'a', + 'ă' => 'a', + 'ắ' => 'a', + 'ằ' => 'a', + 'ẳ' => 'a', + 'ẵ' => 'a', + 'ặ' => 'a', + 'â' => 'a', + 'ấ' => 'a', + 'ầ' => 'a', + 'ẩ' => 'a', + 'ẫ' => 'a', + 'ậ' => 'a', + 'É' => 'E', + 'È' => 'E', + 'Ẻ' => 'E', + 'Ẽ' => 'E', + 'Ẹ' => 'E', + 'Ê' => 'E', + 'Ế' => 'E', + 'Ề' => 'E', + 'Ể' => 'E', + 'Ễ' => 'E', + 'Ệ' => 'E', + 'é' => 'e', + 'è' => 'e', + 'ẻ' => 'e', + 'ẽ' => 'e', + 'ẹ' => 'e', + 'ê' => 'e', + 'ế' => 'e', + 'ề' => 'e', + 'ể' => 'e', + 'ễ' => 'e', + 'ệ' => 'e', + 'Í' => 'I', + 'Ì' => 'I', + 'Ỉ' => 'I', + 'Ĩ' => 'I', + 'Ị' => 'I', + 'í' => 'i', + 'ì' => 'i', + 'ỉ' => 'i', + 'ĩ' => 'i', + 'ị' => 'i', + 'Ó' => 'O', + 'Ò' => 'O', + 'Ỏ' => 'O', + 'Õ' => 'O', + 'Ọ' => 'O', + 'Ô' => 'O', + 'Ố' => 'O', + 'Ồ' => 'O', + 'Ổ' => 'O', + 'Ỗ' => 'O', + 'Ộ' => 'O', + 'Ơ' => 'O', + 'Ớ' => 'O', + 'Ờ' => 'O', + 'Ở' => 'O', + 'Ỡ' => 'O', + 'Ợ' => 'O', + 'ó' => 'o', + 'ò' => 'o', + 'ỏ' => 'o', + 'õ' => 'o', + 'ọ' => 'o', + 'ô' => 'o', + 'ố' => 'o', + 'ồ' => 'o', + 'ổ' => 'o', + 'ỗ' => 'o', + 'ộ' => 'o', + 'ơ' => 'o', + 'ớ' => 'o', + 'ờ' => 'o', + 'ở' => 'o', + 'ỡ' => 'o', + 'ợ' => 'o', + 'Ú' => 'U', + 'Ù' => 'U', + 'Ủ' => 'U', + 'Ũ' => 'U', + 'Ụ' => 'U', + 'Ư' => 'U', + 'Ứ' => 'U', + 'Ừ' => 'U', + 'Ử' => 'U', + 'Ữ' => 'U', + 'Ự' => 'U', + 'ú' => 'u', + 'ù' => 'u', + 'ủ' => 'u', + 'ũ' => 'u', + 'ụ' => 'u', + 'ư' => 'u', + 'ứ' => 'u', + 'ừ' => 'u', + 'ử' => 'u', + 'ữ' => 'u', + 'ự' => 'u', + 'Ý' => 'Y', + 'Ỳ' => 'Y', + 'Ỷ' => 'Y', + 'Ỹ' => 'Y', + 'Ỵ' => 'Y', + 'ý' => 'y', + 'ỳ' => 'y', + 'ỷ' => 'y', + 'ỹ' => 'y', + 'ỵ' => 'y', + 'Đ' => 'D', + 'đ' => 'd', + ], + // Persian (Farsi) + 'fa' => [ + 'ا' => 'a', + 'ب' => 'b', + 'پ' => 'p', + 'ت' => 't', + 'ث' => 's', + 'ج' => 'j', + 'چ' => 'ch', + 'ح' => 'h', + 'خ' => 'kh', + 'د' => 'd', + 'ذ' => 'z', + 'ر' => 'r', + 'ز' => 'z', + 'س' => 's', + 'ش' => 'sh', + 'ص' => 's', + 'ض' => 'z', + 'ط' => 't', + 'ظ' => 'z', + 'ع' => 'a', + 'غ' => 'gh', + 'ف' => 'f', + 'ق' => 'gh', + 'ک' => 'k', + 'گ' => 'g', + 'ل' => 'l', + 'ژ' => 'zh', + 'ك' => 'k', + 'م' => 'm', + 'ن' => 'n', + 'ه' => 'h', + 'و' => 'o', + 'ی' => 'y', + 'آ' => 'a', + '٠' => '0', + '١' => '1', + '٢' => '2', + '٣' => '3', + '٤' => '4', + '٥' => '5', + '٦' => '6', + '٧' => '7', + '٨' => '8', + '٩' => '9', + ], + // Arabic + 'ar' => [ + 'أ' => 'a', + 'ب' => 'b', + 'ت' => 't', + 'ث' => 'th', + 'ج' => 'g', + 'ح' => 'h', + 'خ' => 'kh', + 'د' => 'd', + 'ذ' => 'th', + 'ر' => 'r', + 'ز' => 'z', + 'س' => 's', + 'ش' => 'sh', + 'ص' => 's', + 'ض' => 'd', + 'ط' => 't', + 'ظ' => 'th', + 'ع' => 'aa', + 'غ' => 'gh', + 'ف' => 'f', + 'ق' => 'k', + 'ك' => 'k', + 'ل' => 'l', + 'م' => 'm', + 'ن' => 'n', + 'ه' => 'h', + 'و' => 'o', + 'ي' => 'y', + 'ا' => 'a', + 'إ' => 'a', + 'آ' => 'a', + 'ؤ' => 'o', + 'ئ' => 'y', + 'ء' => 'aa', + '٠' => '0', + '١' => '1', + '٢' => '2', + '٣' => '3', + '٤' => '4', + '٥' => '5', + '٦' => '6', + '٧' => '7', + '٨' => '8', + '٩' => '9', + ], + // Serbian + 'sr' => [ + 'đ' => 'dj', + 'ž' => 'z', + 'ć' => 'c', + 'č' => 'c', + 'š' => 's', + 'Đ' => 'Dj', + 'Ž' => 'Z', + 'Ć' => 'C', + 'Č' => 'C', + 'Š' => 'S', + 'а' => 'a', + 'б' => 'b', + 'в' => 'v', + 'г' => 'g', + 'д' => 'd', + 'ђ' => 'dj', + 'е' => 'e', + 'ж' => 'z', + 'з' => 'z', + 'и' => 'i', + 'ј' => 'j', + 'к' => 'k', + 'л' => 'l', + 'љ' => 'lj', + 'м' => 'm', + 'н' => 'n', + 'њ' => 'nj', + 'о' => 'o', + 'п' => 'p', + 'р' => 'r', + 'с' => 's', + 'т' => 't', + 'ћ' => 'c', + 'у' => 'u', + 'ф' => 'f', + 'х' => 'h', + 'ц' => 'c', + 'ч' => 'c', + 'џ' => 'dz', + 'ш' => 's', + 'А' => 'A', + 'Б' => 'B', + 'В' => 'V', + 'Г' => 'G', + 'Д' => 'D', + 'Ђ' => 'Dj', + 'Е' => 'E', + 'Ж' => 'Z', + 'З' => 'Z', + 'И' => 'I', + 'Ј' => 'j', + 'К' => 'K', + 'Л' => 'L', + 'Љ' => 'Lj', + 'М' => 'M', + 'Н' => 'N', + 'Њ' => 'Nj', + 'О' => 'O', + 'П' => 'P', + 'Р' => 'R', + 'С' => 'S', + 'Т' => 'T', + 'Ћ' => 'C', + 'У' => 'U', + 'Ф' => 'F', + 'Х' => 'H', + 'Ц' => 'C', + 'Ч' => 'C', + 'Џ' => 'Dz', + 'Ш' => 'S', + ], + // Serbian - Cyrillic + 'sr__cyr' => [ + 'а' => 'a', + 'б' => 'b', + 'в' => 'v', + 'г' => 'g', + 'д' => 'd', + 'ђ' => 'dj', + 'е' => 'e', + 'ж' => 'z', + 'з' => 'z', + 'и' => 'i', + 'ј' => 'j', + 'к' => 'k', + 'л' => 'l', + 'љ' => 'lj', + 'м' => 'm', + 'н' => 'n', + 'њ' => 'nj', + 'о' => 'o', + 'п' => 'p', + 'р' => 'r', + 'с' => 's', + 'т' => 't', + 'ћ' => 'c', + 'у' => 'u', + 'ф' => 'f', + 'х' => 'h', + 'ц' => 'c', + 'ч' => 'c', + 'џ' => 'dz', + 'ш' => 's', + 'А' => 'A', + 'Б' => 'B', + 'В' => 'V', + 'Г' => 'G', + 'Д' => 'D', + 'Ђ' => 'Dj', + 'Е' => 'E', + 'Ж' => 'Z', + 'З' => 'Z', + 'И' => 'I', + 'Ј' => 'j', + 'К' => 'K', + 'Л' => 'L', + 'Љ' => 'Lj', + 'М' => 'M', + 'Н' => 'N', + 'Њ' => 'Nj', + 'О' => 'O', + 'П' => 'P', + 'Р' => 'R', + 'С' => 'S', + 'Т' => 'T', + 'Ћ' => 'C', + 'У' => 'U', + 'Ф' => 'F', + 'Х' => 'H', + 'Ц' => 'C', + 'Ч' => 'C', + 'Џ' => 'Dz', + 'Ш' => 'S', + ], + // Serbian - Latin + 'sr__lat' => [ + 'đ' => 'dj', + 'ž' => 'z', + 'ć' => 'c', + 'č' => 'c', + 'š' => 's', + 'Đ' => 'Dj', + 'Ž' => 'Z', + 'Ć' => 'C', + 'Č' => 'C', + 'Š' => 'S', + ], + // Azerbaijani + 'az' => [ + 'ç' => 'c', + 'ə' => 'e', + 'ğ' => 'g', + 'ı' => 'i', + 'ö' => 'o', + 'ş' => 's', + 'ü' => 'u', + 'Ç' => 'C', + 'Ə' => 'E', + 'Ğ' => 'G', + 'İ' => 'I', + 'Ö' => 'O', + 'Ş' => 'S', + 'Ü' => 'U', + ], + // Slovak + 'sk' => [ + 'á' => 'a', + 'ä' => 'a', + 'č' => 'c', + 'ď' => 'd', + 'é' => 'e', + 'í' => 'i', + 'ľ' => 'l', + 'ĺ' => 'l', + 'ň' => 'n', + 'ó' => 'o', + 'ô' => 'o', + 'ŕ' => 'r', + 'š' => 's', + 'ť' => 't', + 'ú' => 'u', + 'ý' => 'y', + 'ž' => 'z', + 'Á' => 'A', + 'Ä' => 'A', + 'Č' => 'C', + 'Ď' => 'D', + 'É' => 'E', + 'Í' => 'I', + 'Ľ' => 'L', + 'Ĺ' => 'L', + 'Ň' => 'N', + 'Ó' => 'O', + 'Ô' => 'O', + 'Ŕ' => 'R', + 'Š' => 'S', + 'Ť' => 'T', + 'Ú' => 'U', + 'Ý' => 'Y', + 'Ž' => 'Z', + ], + // French + 'fr' => [ + 'Æ' => 'AE', + 'æ' => 'ae', + 'Œ' => 'OE', + 'œ' => 'oe', + 'â' => 'a', + 'Â' => 'A', + 'à' => 'a', + 'À' => 'A', + 'ä' => 'a', + 'Ä' => 'A', + 'ç' => 'c', + 'Ç' => 'C', + 'é' => 'e', + 'É' => 'E', + 'ê' => 'e', + 'Ê' => 'E', + 'ë' => 'e', + 'Ë' => 'E', + 'è' => 'e', + 'È' => 'E', + 'ï' => 'i', + 'î' => 'i', + 'Ï' => 'I', + 'Î' => 'I', + 'ÿ' => 'y', + 'Ÿ' => 'Y', + 'ô' => 'o', + 'Ô' => 'O', + 'ö' => 'o', + 'Ö' => 'O', + 'û' => 'u', + 'Û' => 'U', + 'ù' => 'u', + 'Ù' => 'U', + 'ü' => 'u', + 'Ü' => 'U', + ], + // Austrian (French) + 'fr_at' => [ + 'ß' => 'sz', + 'ẞ' => 'SZ', + 'Æ' => 'AE', + 'æ' => 'ae', + 'Œ' => 'OE', + 'œ' => 'oe', + 'â' => 'a', + 'Â' => 'A', + 'à' => 'a', + 'À' => 'A', + 'ä' => 'a', + 'Ä' => 'A', + 'ç' => 'c', + 'Ç' => 'C', + 'é' => 'e', + 'É' => 'E', + 'ê' => 'e', + 'Ê' => 'E', + 'ë' => 'e', + 'Ë' => 'E', + 'è' => 'e', + 'È' => 'E', + 'ï' => 'i', + 'î' => 'i', + 'Ï' => 'I', + 'Î' => 'I', + 'ÿ' => 'y', + 'Ÿ' => 'Y', + 'ô' => 'o', + 'Ô' => 'O', + 'ö' => 'o', + 'Ö' => 'O', + 'û' => 'u', + 'Û' => 'U', + 'ù' => 'u', + 'Ù' => 'U', + 'ü' => 'u', + 'Ü' => 'U', + ], + // Switzerland (French) + 'fr_ch' => [ + 'ß' => 'ss', + 'ẞ' => 'SS', + 'Æ' => 'AE', + 'æ' => 'ae', + 'Œ' => 'OE', + 'œ' => 'oe', + 'â' => 'a', + 'Â' => 'A', + 'à' => 'a', + 'À' => 'A', + 'ä' => 'a', + 'Ä' => 'A', + 'ç' => 'c', + 'Ç' => 'C', + 'é' => 'e', + 'É' => 'E', + 'ê' => 'e', + 'Ê' => 'E', + 'ë' => 'e', + 'Ë' => 'E', + 'è' => 'e', + 'È' => 'E', + 'ï' => 'i', + 'î' => 'i', + 'Ï' => 'I', + 'Î' => 'I', + 'ÿ' => 'y', + 'Ÿ' => 'Y', + 'ô' => 'o', + 'Ô' => 'O', + 'ö' => 'o', + 'Ö' => 'O', + 'û' => 'u', + 'Û' => 'U', + 'ù' => 'u', + 'Ù' => 'U', + 'ü' => 'u', + 'Ü' => 'U', + ], + // German + 'de' => [ + 'Ä' => 'Ae', + 'Ö' => 'Oe', + 'Ü' => 'Ue', + 'ä' => 'ae', + 'ö' => 'oe', + 'ü' => 'ue', + 'ß' => 'ss', + 'ẞ' => 'SS', + ], + // Austrian (German) + 'de_at' => [ + 'Ä' => 'Ae', + 'Ö' => 'Oe', + 'Ü' => 'Ue', + 'ä' => 'ae', + 'ö' => 'oe', + 'ü' => 'ue', + 'ß' => 'sz', + 'ẞ' => 'SZ', + ], + // Switzerland (German) + 'de_ch' => [ + 'Ä' => 'Ae', + 'Ö' => 'Oe', + 'Ü' => 'Ue', + 'ä' => 'ae', + 'ö' => 'oe', + 'ü' => 'ue', + 'ß' => 'ss', + 'ẞ' => 'SS', + ], + // Bengali (Bangla) + 'bn' => [ + 'ভ্ল' => 'vl', + 'পশ' => 'psh', + 'ব্ধ' => 'bdh', + 'ব্জ' => 'bj', + 'ব্দ' => 'bd', + 'ব্ব' => 'bb', + 'ব্ল' => 'bl', + 'ভ' => 'v', + 'ব' => 'b', + 'চ্ঞ' => 'cNG', + 'চ্ছ' => 'cch', + 'চ্চ' => 'cc', + 'ছ' => 'ch', + 'চ' => 'c', + 'ধ্ন' => 'dhn', + 'ধ্ম' => 'dhm', + 'দ্ঘ' => 'dgh', + 'দ্ধ' => 'ddh', + 'দ্ভ' => 'dv', + 'দ্ম' => 'dm', + 'ড্ড' => 'DD', + 'ঢ' => 'Dh', + 'ধ' => 'dh', + 'দ্গ' => 'dg', + 'দ্দ' => 'dd', + 'ড' => 'D', + 'দ' => 'd', + '।' => '.', + 'ঘ্ন' => 'Ghn', + 'গ্ধ' => 'Gdh', + 'গ্ণ' => 'GN', + 'গ্ন' => 'Gn', + 'গ্ম' => 'Gm', + 'গ্ল' => 'Gl', + 'জ্ঞ' => 'jNG', + 'ঘ' => 'Gh', + 'গ' => 'g', + 'হ্ণ' => 'hN', + 'হ্ন' => 'hn', + 'হ্ম' => 'hm', + 'হ্ল' => 'hl', + 'হ' => 'h', + 'জ্ঝ' => 'jjh', + 'ঝ' => 'jh', + 'জ্জ' => 'jj', + 'জ' => 'j', + 'ক্ষ্ণ' => 'kxN', + 'ক্ষ্ম' => 'kxm', + 'ক্ষ' => 'ksh', + 'কশ' => 'ksh', + 'ক্ক' => 'kk', + 'ক্ট' => 'kT', + 'ক্ত' => 'kt', + 'ক্ল' => 'kl', + 'ক্স' => 'ks', + 'খ' => 'kh', + 'ক' => 'k', + 'ল্ভ' => 'lv', + 'ল্ধ' => 'ldh', + 'লখ' => 'lkh', + 'লঘ' => 'lgh', + 'লফ' => 'lph', + 'ল্ক' => 'lk', + 'ল্গ' => 'lg', + 'ল্ট' => 'lT', + 'ল্ড' => 'lD', + 'ল্প' => 'lp', + 'ল্ম' => 'lm', + 'ল্ল' => 'll', + 'ল্ব' => 'lb', + 'ল' => 'l', + 'ম্থ' => 'mth', + 'ম্ফ' => 'mf', + 'ম্ভ' => 'mv', + 'মপ্ল' => 'mpl', + 'ম্ন' => 'mn', + 'ম্প' => 'mp', + 'ম্ম' => 'mm', + 'ম্ল' => 'ml', + 'ম্ব' => 'mb', + 'ম' => 'm', + '০' => '0', + '১' => '1', + '২' => '2', + '৩' => '3', + '৪' => '4', + '৫' => '5', + '৬' => '6', + '৭' => '7', + '৮' => '8', + '৯' => '9', + 'ঙ্ক্ষ' => 'Ngkx', + 'ঞ্ছ' => 'nch', + 'ঙ্ঘ' => 'ngh', + 'ঙ্খ' => 'nkh', + 'ঞ্ঝ' => 'njh', + 'ঙ্গৌ' => 'ngOU', + 'ঙ্গৈ' => 'ngOI', + 'ঞ্চ' => 'nc', + 'ঙ্ক' => 'nk', + 'ঙ্ষ' => 'Ngx', + 'ঙ্গ' => 'ngo', + 'ঙ্ম' => 'Ngm', + 'ঞ্জ' => 'nj', + 'ন্ধ' => 'ndh', + 'ন্ঠ' => 'nTh', + 'ণ্ঠ' => 'NTh', + 'ন্থ' => 'nth', + 'ঙ্গা' => 'nga', + 'ঙ্গি' => 'ngi', + 'ঙ্গী' => 'ngI', + 'ঙ্গু' => 'ngu', + 'ঙ্গূ' => 'ngU', + 'ঙ্গে' => 'nge', + 'ঙ্গো' => 'ngO', + 'ণ্ঢ' => 'NDh', + 'নশ' => 'nsh', + 'ঙর' => 'Ngr', + 'ঞর' => 'NGr', + 'ংর' => 'ngr', + 'ঙ' => 'Ng', + 'ঞ' => 'NG', + 'ং' => 'ng', + 'ন্ন' => 'nn', + 'ণ্ণ' => 'NN', + 'ণ্ন' => 'Nn', + 'ন্ম' => 'nm', + 'ণ্ম' => 'Nm', + 'ন্দ' => 'nd', + 'ন্ট' => 'nT', + 'ণ্ট' => 'NT', + 'ন্ড' => 'nD', + 'ণ্ড' => 'ND', + 'ন্ত' => 'nt', + 'ন্স' => 'ns', + 'ন' => 'n', + 'ণ' => 'N', + 'ৈ' => 'OI', + 'ৌ' => 'OU', + 'ো' => 'O', + 'ঐ' => 'OI', + 'ঔ' => 'OU', + 'অ' => 'o', + 'ও' => 'oo', + 'ফ্ল' => 'fl', + 'প্ট' => 'pT', + 'প্ত' => 'pt', + 'প্ন' => 'pn', + 'প্প' => 'pp', + 'প্ল' => 'pl', + 'প্স' => 'ps', + 'ফ' => 'f', + 'প' => 'p', + 'ৃ' => 'rri', + 'ঋ' => 'rri', + 'রর‍্য' => 'rry', + '্র্য' => 'ry', + '্রর' => 'rr', + 'ড়্গ' => 'Rg', + 'ঢ়' => 'Rh', + 'ড়' => 'R', + 'র' => 'r', + '্র' => 'r', + 'শ্ছ' => 'Sch', + 'ষ্ঠ' => 'ShTh', + 'ষ্ফ' => 'Shf', + 'স্ক্ল' => 'skl', + 'স্খ' => 'skh', + 'স্থ' => 'sth', + 'স্ফ' => 'sf', + 'শ্চ' => 'Sc', + 'শ্ত' => 'St', + 'শ্ন' => 'Sn', + 'শ্ম' => 'Sm', + 'শ্ল' => 'Sl', + 'ষ্ক' => 'Shk', + 'ষ্ট' => 'ShT', + 'ষ্ণ' => 'ShN', + 'ষ্প' => 'Shp', + 'ষ্ম' => 'Shm', + 'স্প্ল' => 'spl', + 'স্ক' => 'sk', + 'স্ট' => 'sT', + 'স্ত' => 'st', + 'স্ন' => 'sn', + 'স্প' => 'sp', + 'স্ম' => 'sm', + 'স্ল' => 'sl', + 'শ' => 'S', + 'ষ' => 'Sh', + 'স' => 's', + 'ু' => 'u', + 'উ' => 'u', + 'অ্য' => 'oZ', + 'ত্থ' => 'tth', + 'ৎ' => 'tt', + 'ট্ট' => 'TT', + 'ট্ম' => 'Tm', + 'ঠ' => 'Th', + 'ত্ন' => 'tn', + 'ত্ম' => 'tm', + 'থ' => 'th', + 'ত্ত' => 'tt', + 'ট' => 'T', + 'ত' => 't', + 'অ্যা' => 'AZ', + 'া' => 'a', + 'আ' => 'a', + 'য়া' => 'ya', + 'য়' => 'y', + 'ি' => 'i', + 'ই' => 'i', + 'ী' => 'ee', + 'ঈ' => 'ee', + 'ূ' => 'uu', + 'ঊ' => 'uu', + 'ে' => 'e', + 'এ' => 'e', + 'য' => 'z', + '্য' => 'Z', + 'ইয়' => 'y', + 'ওয়' => 'w', + '্ব' => 'w', + 'এক্স' => 'x', + 'ঃ' => ':', + 'ঁ' => 'nn', + '্‌' => '', + ], + // English + 'en' => [ + ], + // Latin (+ Cyrillic ?) chars + // + // -> Mix of languages, but we need to keep this here, so that different languages can handle there own behavior. + 'latin' => [ + '˚' => '0', + '¹' => '1', + '²' => '2', + '³' => '3', + '⁴' => '4', + '⁵' => '5', + '⁶' => '6', + '⁷' => '7', + '⁸' => '8', + '⁹' => '9', + '₀' => '0', + '₁' => '1', + '₂' => '2', + '₃' => '3', + '₄' => '4', + '₅' => '5', + '₆' => '6', + '₇' => '7', + '₈' => '8', + '₉' => '9', + '௦' => '0', + '௧' => '1', + '௨' => '2', + '௩' => '3', + '௪' => '4', + '௫' => '5', + '௬' => '6', + '௭' => '7', + '௮' => '8', + '௯' => '9', + '௰' => '10', + '௱' => '100', + '௲' => '1000', + 'Ꜳ' => 'AA', + 'ꜳ' => 'aa', + 'Æ' => 'AE', + 'æ' => 'ae', + 'Ǽ' => 'AE', + 'ǽ' => 'ae', + 'Ꜵ' => 'AO', + 'ꜵ' => 'ao', + 'Ꜷ' => 'AU', + 'ꜷ' => 'au', + 'Ꜹ' => 'AV', + 'ꜹ' => 'av', + 'Ꜻ' => 'av', + 'ꜻ' => 'av', + 'Ꜽ' => 'AY', + 'ꜽ' => 'ay', + 'ȸ' => 'db', + 'ʣ' => 'dz', + 'ʥ' => 'dz', + 'ʤ' => 'dezh', + '🙰' => 'et', + 'ff' => 'ff', + 'ffi' => 'ffi', + 'ffl' => 'ffl', + 'fi' => 'fi', + 'fl' => 'fl', + 'ʩ' => 'feng', + 'IJ' => 'IJ', + 'ij' => 'ij', + 'ʪ' => 'ls', + 'ʫ' => 'lz', + 'ɮ' => 'lezh', + 'ȹ' => 'qp', + 'ʨ' => 'tc', + 'ʦ' => 'ts', + 'ʧ' => 'tesh', + 'Œ' => 'OE', + 'œ' => 'oe', + 'Ꝏ' => 'OO', + 'ꝏ' => 'oo', + 'ẞ' => 'SS', + 'ß' => 'ss', + 'st' => 'st', + 'ſt' => 'st', + 'Ꜩ' => 'TZ', + 'ꜩ' => 'tz', + 'ᵫ' => 'ue', + 'Aι' => 'Ai', + 'αι' => 'ai', + 'Ει' => 'Ei', + 'ει' => 'ei', + 'Οι' => 'Oi', + 'οι' => 'oi', + 'Ου' => 'Oy', + 'ου' => 'oy', + 'Υι' => 'Yi', + 'υι' => 'yi', + 'ἀ' => 'a', + 'ἁ' => 'a', + 'ἂ' => 'a', + 'ἃ' => 'a', + 'ἄ' => 'a', + 'ἅ' => 'a', + 'ἆ' => 'a', + 'ἇ' => 'a', + 'Ἀ' => 'A', + 'Ἁ' => 'A', + 'Ἂ' => 'A', + 'Ἃ' => 'A', + 'Ἄ' => 'A', + 'Ἅ' => 'A', + 'Ἆ' => 'A', + 'Ἇ' => 'A', + 'ᾰ' => 'a', + 'ᾱ' => 'a', + 'ᾲ' => 'a', + 'ᾳ' => 'a', + 'ᾴ' => 'a', + 'ᾶ' => 'a', + 'ᾷ' => 'a', + 'Ᾰ' => 'A', + 'Ᾱ' => 'A', + 'Ὰ' => 'A', + 'Ά' => 'A', + 'ᾼ' => 'A', + 'Ä' => 'A', + 'ä' => 'a', + 'À' => 'A', + 'à' => 'a', + 'Á' => 'A', + 'á' => 'a', + 'Â' => 'A', + 'â' => 'a', + 'Ã' => 'A', + 'ã' => 'a', + 'A̧' => 'A', + 'a̧' => 'a', + 'Ą' => 'A', + 'ą' => 'a', + 'Ⱥ' => 'A', + 'ⱥ' => 'a', + 'Å' => 'A', + 'å' => 'a', + 'Ǻ' => 'A', + 'ǻ' => 'a', + 'Ă' => 'A', + 'ă' => 'a', + 'Ǎ' => 'A', + 'ǎ' => 'a', + 'Ȧ' => 'A', + 'ȧ' => 'a', + 'Ạ' => 'A', + 'ạ' => 'a', + 'Ā' => 'A', + 'ā' => 'a', + 'ª' => 'a', + 'Ɓ' => 'B', + 'Ѣ' => 'E', + 'ѣ' => 'e', + 'Ç' => 'C', + 'ç' => 'c', + 'Ĉ' => 'C', + 'ĉ' => 'c', + 'C̈' => 'C', + 'c̈' => 'c', + 'C̨' => 'C', + 'c̨' => 'c', + 'Ȼ' => 'C', + 'ȼ' => 'c', + 'Č' => 'C', + 'č' => 'c', + 'Ć' => 'C', + 'ć' => 'c', + 'C̀' => 'C', + 'c̀' => 'c', + 'Ċ' => 'C', + 'ċ' => 'c', + 'C̣' => 'C', + 'c̣' => 'c', + 'C̄' => 'C', + 'c̄' => 'c', + 'C̃' => 'C', + 'c̃' => 'c', + 'Ð' => 'D', + 'Đ' => 'D', + 'ð' => 'd', + 'đ' => 'd', + 'È' => 'E', + 'É' => 'E', + 'Ê' => 'E', + 'Ë' => 'E', + 'Ĕ' => 'E', + 'Ė' => 'E', + 'Ȩ' => 'E', + 'ȩ' => 'e', + 'Ę' => 'E', + 'ę' => 'e', + 'Ɇ' => 'E', + 'ɇ' => 'e', + 'Ě' => 'E', + 'ě' => 'e', + 'Ẹ' => 'E', + 'ẹ' => 'e', + 'Ē' => 'E', + 'ē' => 'e', + 'Ẽ' => 'E', + 'ẽ' => 'e', + 'è' => 'e', + 'é' => 'e', + 'ê' => 'e', + 'ë' => 'e', + 'ĕ' => 'e', + 'ė' => 'e', + 'ƒ' => 'f', + 'Ѳ' => 'F', + 'ѳ' => 'f', + 'Ĝ' => 'G', + 'Ġ' => 'G', + 'ĝ' => 'g', + 'ġ' => 'g', + 'Ĥ' => 'H', + 'Ħ' => 'H', + 'ĥ' => 'h', + 'ħ' => 'h', + 'Ì' => 'I', + 'Í' => 'I', + 'Î' => 'I', + 'Ï' => 'I', + 'Ĩ' => 'I', + 'Ĭ' => 'I', + 'Ǐ' => 'I', + 'Į' => 'I', + 'ì' => 'i', + 'í' => 'i', + 'î' => 'i', + 'ï' => 'i', + 'ĩ' => 'i', + 'ĭ' => 'i', + 'ǐ' => 'i', + 'į' => 'i', + 'І' => 'I', + 'і' => 'i', + 'I̧' => 'I', + 'i̧' => 'i', + 'Ɨ' => 'I', + 'ɨ' => 'i', + 'İ' => 'I', + 'i' => 'i', + 'Ị' => 'I', + 'ị' => 'i', + 'Ī' => 'I', + 'ī' => 'i', + 'Ĵ' => 'J', + 'ĵ' => 'j', + 'J́́' => 'J', + 'j́' => 'j', + 'J̀̀' => 'J', + 'j̀' => 'j', + 'J̈' => 'J', + 'j̈' => 'j', + 'J̧' => 'J', + 'j̧' => 'j', + 'J̨' => 'J', + 'j̨' => 'j', + 'Ɉ' => 'J', + 'ɉ' => 'j', + 'J̌' => 'J', + 'ǰ' => 'j', + 'J̇' => 'J', + 'j' => 'j', + 'J̣' => 'J', + 'j̣' => 'j', + 'J̄' => 'J', + 'j̄' => 'j', + 'J̃' => 'J', + 'j̃' => 'j', + 'Й' => 'i', + 'й' => 'i', + 'ĸ' => 'k', + 'Ĺ' => 'L', + 'Ľ' => 'L', + 'Ŀ' => 'L', + 'ĺ' => 'l', + 'ľ' => 'l', + 'ŀ' => 'l', + 'L̀' => 'L', + 'l̀' => 'l', + 'L̂' => 'L', + 'l̂' => 'l', + 'L̈' => 'L', + 'l̈' => 'l', + 'Ļ' => 'L', + 'ļ' => 'l', + 'L̨' => 'L', + 'l̨' => 'l', + 'Ł' => 'L', + 'ł' => 'l', + 'Ƚ' => 'L', + 'ƚ' => 'l', + 'L̇' => 'L', + 'l̇' => 'l', + 'Ḷ' => 'L', + 'ḷ' => 'l', + 'L̄' => 'L', + 'l̄' => 'l', + 'L̃' => 'L', + 'l̃' => 'l', + 'Ñ' => 'N', + 'ñ' => 'n', + 'Ŋ' => 'N', + 'ŋ' => 'n', + 'ʼn' => 'n', + 'Ń' => 'N', + 'ń' => 'n', + 'Ǹ' => 'N', + 'ǹ' => 'n', + 'N̂' => 'N', + 'n̂' => 'n', + 'N̈' => 'N', + 'n̈' => 'n', + 'Ņ' => 'N', + 'ņ' => 'n', + 'N̨' => 'N', + 'n̨' => 'n', + 'Ꞥ' => 'N', + 'ꞥ' => 'n', + 'Ň' => 'N', + 'ň' => 'n', + 'Ṅ' => 'N', + 'ṅ' => 'n', + 'Ṇ' => 'N', + 'ṇ' => 'n', + 'N̄' => 'N', + 'n̄' => 'n', + 'Ö' => 'O', + 'Ò' => 'O', + 'Ó' => 'O', + 'Ô' => 'O', + 'Õ' => 'O', + 'Ō' => 'O', + 'Ŏ' => 'O', + 'Ǒ' => 'O', + 'Ő' => 'O', + 'Ơ' => 'O', + 'Ø' => 'O', + 'Ǿ' => 'O', + 'ö' => 'o', + 'ò' => 'o', + 'ó' => 'o', + 'ô' => 'o', + 'õ' => 'o', + 'ō' => 'o', + 'ŏ' => 'o', + 'ǒ' => 'o', + 'ő' => 'o', + 'ơ' => 'o', + 'ø' => 'o', + 'ǿ' => 'o', + 'º' => 'o', + 'O̧' => 'O', + 'o̧' => 'o', + 'Ǫ' => 'O', + 'ǫ' => 'o', + 'Ɵ' => 'O', + 'ɵ' => 'o', + 'Ȯ' => 'O', + 'ȯ' => 'o', + 'Ọ' => 'O', + 'ọ' => 'o', + 'Ŕ' => 'R', + 'Ŗ' => 'R', + 'ŕ' => 'r', + 'ŗ' => 'r', + 'Ŝ' => 'S', + 'Ș' => 'S', + 'ș' => 's', + 'Ś' => 'S', + 'ś' => 's', + 'S̀' => 'S', + 's̀' => 's', + 'Ŝ̀' => 'S', + 'ŝ' => 's', + 'S̈' => 'S', + 's̈' => 's', + 'Ş' => 'S', + 'ş' => 's', + 'S̨' => 'S', + 's̨' => 's', + 'Ꞩ' => 'S', + 'ꞩ' => 's', + 'Š' => 'S', + 'š' => 's', + 'Ṡ' => 'S', + 'ṡ' => 's', + 'Ṣ' => 'S', + 'ṣ' => 's', + 'S̄' => 'S', + 's̄' => 's', + 'S̃' => 'S', + 's̃' => 's', + 'ſ' => 's', + 'Ţ' => 'T', + 'Ț' => 'T', + 'Ŧ' => 'T', + 'Þ' => 'TH', + 'ţ' => 't', + 'ț' => 't', + 'ŧ' => 't', + 'þ' => 'th', + 'T́' => 'T', + 't́' => 't', + 'T̀' => 'T', + 't̀' => 't', + 'T̂' => 'T', + 't̂' => 't', + 'T̈' => 'T', + 'ẗ' => 't', + 'T̨' => 'T', + 't̨' => 't', + 'Ⱦ' => 'T', + 'ⱦ' => 't', + 'Ť' => 'T', + 'ť' => 't', + 'Ṫ' => 'T', + 'ṫ' => 't', + 'Ṭ' => 'T', + 'ṭ' => 't', + 'T̄' => 'T', + 't̄' => 't', + 'T̃' => 'T', + 't̃' => 't', + 'Ü' => 'U', + 'Ù' => 'U', + 'Ú' => 'U', + 'Û' => 'U', + 'Ũ' => 'U', + 'Ŭ' => 'U', + 'Ű' => 'U', + 'Ų' => 'U', + 'Ư' => 'U', + 'Ǔ' => 'U', + 'Ǖ' => 'U', + 'Ǘ' => 'U', + 'Ǚ' => 'U', + 'Ǜ' => 'U', + 'ü' => 'u', + 'ù' => 'u', + 'ú' => 'u', + 'û' => 'u', + 'ũ' => 'u', + 'ŭ' => 'u', + 'ű' => 'u', + 'ų' => 'u', + 'ư' => 'u', + 'ǔ' => 'u', + 'ǖ' => 'u', + 'ǘ' => 'u', + 'ǚ' => 'u', + 'ǜ' => 'u', + 'U̧' => 'U', + 'u̧' => 'u', + 'Ʉ' => 'U', + 'ʉ' => 'u', + 'U̇' => 'U', + 'u̇' => 'u', + 'Ụ' => 'U', + 'ụ' => 'u', + 'Ū' => 'U', + 'ū' => 'u', + 'Ʊ' => 'U', + 'ʊ' => 'u', + 'Ŵ' => 'W', + 'ŵ' => 'w', + 'Ẁ' => 'W', + 'ẁ' => 'w', + 'Ẃ' => 'W', + 'ẃ' => 'w', + 'Ẅ' => 'W', + 'ẅ' => 'w', + 'Ѵ' => 'I', + 'ѵ' => 'i', + 'Ꙗ' => 'Ja', + 'ꙗ' => 'ja', + 'Є' => 'Je', + 'є' => 'je', + 'Ѥ' => 'Je', + 'ѥ' => 'je', + 'Ѕ' => 'Dz', + 'ѕ' => 'dz', + 'Ꙋ' => 'U', + 'ꙋ' => 'u', + 'Ѡ' => 'O', + 'ѡ' => 'o', + 'Ѿ' => 'Ot', + 'ѿ' => 'ot', + 'Ѫ' => 'U', + 'ѫ' => 'u', + 'Ѧ' => 'Ja', + 'ѧ' => 'ja', + 'Ѭ' => 'Ju', + 'ѭ' => 'ju', + 'Ѩ' => 'Ja', + 'ѩ' => 'Ja', + 'Ѯ' => 'Ks', + 'ѯ' => 'ks', + 'Ѱ' => 'Ps', + 'ѱ' => 'ps', + 'Х' => 'X', + 'х' => 'x', + 'Ý' => 'Y', + 'Ÿ' => 'Y', + 'Ŷ' => 'Y', + 'ý' => 'y', + 'ÿ' => 'y', + 'ŷ' => 'y', + 'Ỳ' => 'Y', + 'ỳ' => 'y', + 'Y̧' => 'Y', + 'y̧' => 'y', + 'Y̨' => 'Y', + 'y̨' => 'y', + 'Ɏ' => 'Y', + 'ɏ' => 'y', + 'Y̌' => 'Y', + 'y̌' => 'y', + 'Ẏ' => 'Y', + 'ẏ' => 'y', + 'Ỵ' => 'Y', + 'ỵ' => 'y', + 'Ȳ' => 'Y', + 'ȳ' => 'y', + 'Ỹ' => 'Y', + 'ỹ' => 'y', + 'Щ' => 'Shh', + 'щ' => 'shh', + 'Ź' => 'Z', + 'ź' => 'z', + 'Z̀' => 'Z', + 'z̀' => 'z', + 'Ẑ' => 'Z', + 'ẑ' => 'z', + 'Z̈' => 'Z', + 'z̈' => 'z', + 'Z̧' => 'Z', + 'z̧' => 'z', + 'Z̨' => 'Z', + 'z̨' => 'z', + 'Ƶ' => 'Z', + 'ƶ' => 'z', + 'Ž' => 'Z', + 'ž' => 'z', + 'Ż' => 'Z', + 'ż' => 'z', + 'Ẓ' => 'Z', + 'ẓ' => 'z', + 'Z̄' => 'Z', + 'z̄' => 'z', + 'Z̃' => 'Z', + 'z̃' => 'z', + ], + // whitespace chars + ' ' => [ + "\xc2\xa0" => ' ', // 'NO-BREAK SPACE' + "\xe1\x9a\x80" => ' ', // 'OGHAM SPACE MARK' + "\xe2\x80\x80" => ' ', // 'EN QUAD' + "\xe2\x80\x81" => ' ', // 'EM QUAD' + "\xe2\x80\x82" => ' ', // 'EN SPACE' + "\xe2\x80\x83" => ' ', // 'EM SPACE' + "\xe2\x80\x84" => ' ', // 'THREE-PER-EM SPACE' + "\xe2\x80\x85" => ' ', // 'FOUR-PER-EM SPACE' + "\xe2\x80\x86" => ' ', // 'SIX-PER-EM SPACE' + "\xe2\x80\x87" => ' ', // 'FIGURE SPACE' + "\xe2\x80\x88" => ' ', // 'PUNCTUATION SPACE' + "\xe2\x80\x89" => ' ', // 'THIN SPACE' + "\xe2\x80\x8a" => ' ', // 'HAIR SPACE' + "\xe2\x80\xa8" => ' ', // 'LINE SEPARATOR' + "\xe2\x80\xa9" => ' ', // 'PARAGRAPH SEPARATOR' + "\xe2\x80\x8b" => ' ', // 'ZERO WIDTH SPACE' + "\xe2\x80\xaf" => ' ', // 'NARROW NO-BREAK SPACE' + "\xe2\x81\x9f" => ' ', // 'MEDIUM MATHEMATICAL SPACE' + "\xe3\x80\x80" => ' ', // 'IDEOGRAPHIC SPACE' + "\xef\xbe\xa0" => ' ', // 'HALFWIDTH HANGUL FILLER' + ], + // commonly used in Word documents + 'msword' => [ + "\xc2\xab" => '<<', // « (U+00AB) in UTF-8 + "\xc2\xbb" => '>>', // » (U+00BB) in UTF-8 + "\xe2\x80\x98" => "'", // ‘ (U+2018) in UTF-8 + "\xe2\x80\x99" => "'", // ’ (U+2019) in UTF-8 + "\xe2\x80\x9a" => "'", // ‚ (U+201A) in UTF-8 + "\xe2\x80\x9b" => "'", // ‛ (U+201B) in UTF-8 + "\xe2\x80\x9c" => '"', // “ (U+201C) in UTF-8 + "\xe2\x80\x9d" => '"', // ” (U+201D) in UTF-8 + "\xe2\x80\x9e" => '"', // „ (U+201E) in UTF-8 + "\xe2\x80\x9f" => '"', // ‟ (U+201F) in UTF-8 + "\xe2\x80\xb9" => "'", // ‹ (U+2039) in UTF-8 + "\xe2\x80\xba" => "'", // › (U+203A) in UTF-8 + "\xe2\x80\x93" => '-', // – (U+2013) in UTF-8 + "\xe2\x80\x94" => '-', // — (U+2014) in UTF-8 + "\xe2\x80\xa6" => '...', // … (U+2026) in UTF-8 + ], + // Currency + // + // url => https://en.wikipedia.org/wiki/Currency_symbol + 'currency_short' => [ + '€' => 'EUR', + '$' => '$', + '₢' => 'Cr', + '₣' => 'Fr.', + '£' => 'PS', + '₤' => 'L.', + 'ℳ' => 'M', + '₥' => 'mil', + '₦' => 'N', + '₧' => 'Pts', + '₨' => 'Rs', + 'රු' => 'LKR', + 'ரூ' => 'LKR', + '௹' => 'Rs', + 'रू' => 'NPR', + '₹' => 'Rs', + '૱' => 'Rs', + '₩' => 'W', + '₪' => 'NS', + '₸' => 'KZT', + '₫' => 'D', + '֏' => 'AMD', + '₭' => 'K', + '₺' => 'TL', + '₼' => 'AZN', + '₮' => 'T', + '₯' => 'Dr', + '₲' => 'PYG', + '₾' => 'GEL', + '₳' => 'ARA', + '₴' => 'UAH', + '₽' => 'RUB', + '₵' => 'GHS', + '₡' => 'CL', + '¢' => 'c', + '¥' => 'YEN', + '円' => 'JPY', + '৳' => 'BDT', + '元' => 'CNY', + '﷼' => 'SAR', + '៛' => 'KR', + '₠' => 'ECU', + '¤' => '$?', + '฿' => 'THB', + '؋' => 'AFN', + ], +]; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/ascii_extras_by_languages.php b/includes/libraries/anti-xss-master/src/voku/helper/data/ascii_extras_by_languages.php new file mode 100644 index 000000000..afe31ae2c --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/ascii_extras_by_languages.php @@ -0,0 +1,759 @@ + [ + '=' => ' gelijk ', + '%' => ' procent ', + '∑' => ' som ', + '∆' => ' delta ', + '∞' => ' oneindig ', + '♥' => ' love ', + '&' => ' en ', + '+' => ' plus ', + ], + // Italian + 'it' => [ + '=' => ' uguale ', + '%' => ' percent ', + '∑' => ' somma ', + '∆' => ' delta ', + '∞' => ' infinito ', + '♥' => ' amore ', + '&' => ' e ', + '+' => ' piu ', + ], + // Macedonian + 'mk' => [ + '=' => ' ednakva ', + '%' => ' procenti ', + '∑' => ' zbir ', + '∆' => ' delta ', + '∞' => ' beskonecnost ', + '♥' => ' loveubov ', + '&' => ' i ', + '+' => ' plus ', + ], + // Portuguese (Brazil) + 'pt' => [ + '=' => ' igual ', + '%' => ' por cento ', + '∑' => ' soma ', + '∆' => ' delta ', + '∞' => ' infinito ', + '♥' => ' amor ', + '&' => ' e ', + '+' => ' mais ', + ], + // Greek(lish) (Elláda) + 'el__greeklish' => [ + '=' => ' isos ', + '%' => ' tois ekato ', + '∑' => ' athroisma ', + '∆' => ' delta ', + '∞' => ' apeiro ', + '♥' => ' agape ', + '&' => ' kai ', + '+' => ' syn ', + ], + // Greek (Elláda) + 'el' => [ + '=' => ' isos ', + '%' => ' tois ekato ', + '∑' => ' athroisma ', + '∆' => ' delta ', + '∞' => ' apeiro ', + '♥' => ' agape ', + '&' => ' kai ', + '+' => ' syn ', + ], + // Hindi + 'hi' => [ + '=' => ' samana ', + '%' => ' paratisata ', + '∑' => ' yoga ', + '∆' => ' dalata ', + '∞' => ' anata ', + '♥' => ' payara ', + '&' => ' aura ', + '+' => ' palasa ', + ], + // Armenian + 'hy' => [ + '=' => ' havasar ', + '%' => ' tvokvos ', + '∑' => ' gvoumar ', + '∆' => ' delta ', + '∞' => ' ansahmanvouthyvoun ', + '♥' => ' ser ', + '&' => ' ev ', + '+' => ' gvoumarats ', + ], + // Swedish + 'sv' => [ + '=' => ' lika ', + '%' => ' procent ', + '∑' => ' summa ', + '∆' => ' delta ', + '∞' => ' oandlighet ', + '♥' => ' alskar ', + '&' => ' och ', + '+' => ' plus ', + ], + // Turkmen + 'tk' => [ + '=' => ' den ', + '%' => ' yuzde ', + '∑' => ' jem ', + '∆' => ' delta ', + '∞' => ' mudimilik ', + '♥' => ' soygi ', + '&' => ' we ', + '+' => ' yzy ', + ], + // Turkish + 'tr' => [ + '=' => ' esit ', + '%' => ' yuzde ', + '∑' => ' Toplam ', + '∆' => ' delta ', + '∞' => ' sonsuzluk ', + '♥' => ' ask ', + '&' => ' ve ', + '+' => ' arti ', + ], + // Bulgarian + 'bg' => [ + '=' => ' raven ', + '%' => ' na sto ', + '∑' => ' suma ', + '∆' => ' delta ', + '∞' => ' bezkrajnost ', + '♥' => ' obicam ', + '&' => ' i ', + '+' => ' plus ', + ], + // Hungarian + 'hu' => [ + '=' => ' Egyenlo ', + '%' => ' Szazalek ', + '∑' => ' osszeg ', + '∆' => ' delta ', + '∞' => ' vegtelenitett ', + '♥' => ' love ', + '&' => ' Es ', + '+' => ' Plusz ', + ], + // Myanmar (Burmese) + 'my' => [ + '=' => ' ttn:ttnnym? ', + '%' => ' raakhngnn:k ', + '∑' => ' ld ', + '∆' => ' m?cwk?n:pe? ', + '∞' => ' ach:m ', + '♥' => ' mettttaa ', + '&' => ' n ', + '+' => ' ape?ng: ', + ], + // Croatian (Hrvatska) + 'hr' => [ + '=' => ' Jednaki ', + '%' => ' Posto ', + '∑' => ' zbroj ', + '∆' => ' Delta ', + '∞' => ' beskonacno ', + '♥' => ' ljubav ', + '&' => ' I ', + '+' => ' Plus ', + ], + // Finnish + 'fi' => [ + '=' => ' Sama ', + '%' => ' Prosenttia ', + '∑' => ' sum ', + '∆' => ' delta ', + '∞' => ' aareton ', + '♥' => ' rakkautta ', + '&' => ' Ja ', + '+' => ' Plus ', + ], + // Georgian (Kartvelian) + 'ka' => [ + '=' => ' tanasts\'ori ', + '%' => ' p\'rotsent\'i ', + '∑' => ' tankha ', + '∆' => ' delt\'a ', + '∞' => ' usasrulo ', + '♥' => ' siq\'varuli ', + '&' => ' da ', + '+' => ' p\'lus ', + ], + // Russian + 'ru' => [ + '=' => ' ravnyj ', + '%' => ' procent ', + '∑' => ' summa ', + '∆' => ' del\'ta ', + '∞' => ' beskonecnost\' ', + '♥' => ' lublu ', + '&' => ' i ', + '+' => ' plus ', + ], + // Russian - GOST 7.79-2000(B) + 'ru__gost_2000_b' => [ + '=' => ' ravnyj ', + '%' => ' procent ', + '∑' => ' summa ', + '∆' => ' del\'ta ', + '∞' => ' beskonecnost\' ', + '♥' => ' lublu ', + '&' => ' i ', + '+' => ' plus ', + ], + // Russian - Passport (2013), ICAO + 'ru__passport_2013' => [ + '=' => ' ravnyj ', + '%' => ' procent ', + '∑' => ' summa ', + '∆' => ' del\'ta ', + '∞' => ' beskonecnost\' ', + '♥' => ' lublu ', + '&' => ' i ', + '+' => ' plus ', + ], + // Ukrainian + 'uk' => [ + '=' => ' rivnij ', + '%' => ' vidsotkiv ', + '∑' => ' suma ', + '∆' => ' del\'ta ', + '∞' => ' neskincennist\' ', + '♥' => ' lubov ', + '&' => ' i ', + '+' => ' plus ', + ], + // Kazakh + 'kk' => [ + '=' => ' ten\' ', + '%' => ' Pajyzdar ', + '∑' => ' zalpy ', + '∆' => ' ajyrmasylyk, ', + '∞' => ' seksiz ', + '♥' => ' mahabbat ', + '&' => ' z@ne ', + '+' => ' plus ', + ], + // Czech + 'cs' => [ + '=' => ' rovnat se ', + '%' => ' procento ', + '∑' => ' soucet ', + '∆' => ' delta ', + '∞' => ' nekonecno ', + '♥' => ' laska ', + '&' => ' a ', + '+' => ' plus ', + ], + // Danish + 'da' => [ + '=' => ' Lige ', + '%' => ' Prozent ', + '∑' => ' sum ', + '∆' => ' delta ', + '∞' => ' uendelig ', + '♥' => ' kaerlighed ', + '&' => ' Og ', + '+' => ' Plus ', + ], + // Polish + 'pl' => [ + '=' => ' rowny ', + '%' => ' procent ', + '∑' => ' suma ', + '∆' => ' delta ', + '∞' => ' nieskonczonosc ', + '♥' => ' milosc ', + '&' => ' i ', + '+' => ' plus ', + ], + // Romanian + 'ro' => [ + '=' => ' egal ', + '%' => ' la suta ', + '∑' => ' suma ', + '∆' => ' delta ', + '∞' => ' infinit ', + '♥' => ' dragoste ', + '&' => ' si ', + '+' => ' la care se adauga ', + ], + // Esperanto + 'eo' => [ + '=' => ' Egalaj ', + '%' => ' Procento ', + '∑' => ' sumo ', + '∆' => ' delto ', + '∞' => ' senfina ', + '♥' => ' amo ', + '&' => ' Kaj ', + '+' => ' Pli ', + ], + // Estonian + 'et' => [ + '=' => ' Vordsed ', + '%' => ' Protsenti ', + '∑' => ' summa ', + '∆' => ' o ', + '∞' => ' loputut ', + '♥' => ' armastus ', + '&' => ' Ja ', + '+' => ' Pluss ', + ], + // Latvian + 'lv' => [ + '=' => ' vienads ', + '%' => ' procents ', + '∑' => ' summa ', + '∆' => ' delta ', + '∞' => ' bezgaliba ', + '♥' => ' milestiba ', + '&' => ' un ', + '+' => ' pluss ', + ], + // Lithuanian + 'lt' => [ + '=' => ' lygus ', + '%' => ' procentu ', + '∑' => ' suma ', + '∆' => ' delta ', + '∞' => ' begalybe ', + '♥' => ' meile ', + '&' => ' ir ', + '+' => ' plius ', + ], + // Norwegian + 'no' => [ + '=' => ' Lik ', + '%' => ' Prosent ', + '∑' => ' sum ', + '∆' => ' delta ', + '∞' => ' uendelig ', + '♥' => ' kjaerlighet ', + '&' => ' Og ', + '+' => ' Pluss ', + ], + // Vietnamese + 'vi' => [ + '=' => ' cong bang ', + '%' => ' phan tram ', + '∑' => ' tong so ', + '∆' => ' dong bang ', + '∞' => ' vo cuc ', + '♥' => ' Yeu ', + '&' => ' va ', + '+' => ' them ', + ], + // Arabic + 'ar' => [ + '=' => ' mtsawy ', + '%' => ' nsbh mywyh ', + '∑' => ' mjmw\' ', + '∆' => ' dlta ', + '∞' => ' ma la nhayt ', + '♥' => ' hb ', + '&' => ' w ', + '+' => ' zayd ', + ], + // Persian (Farsi) + 'fa' => [ + '=' => ' brabr ', + '%' => ' dr sd ', + '∑' => ' mjmw\' ', + '∆' => ' dlta ', + '∞' => ' by nhayt ', + '♥' => ' \'shq ', + '&' => ' w ', + '+' => ' bh \'lawh ', + ], + // Serbian + 'sr' => [ + '=' => ' jednak ', + '%' => ' procenat ', + '∑' => ' zbir ', + '∆' => ' delta ', + '∞' => ' beskraj ', + '♥' => ' lubav ', + '&' => ' i ', + '+' => ' vise ', + ], + // Serbian - Cyrillic + 'sr__cyr' => [ + '=' => ' jednak ', + '%' => ' procenat ', + '∑' => ' zbir ', + '∆' => ' delta ', + '∞' => ' beskraj ', + '♥' => ' lubav ', + '&' => ' i ', + '+' => ' vise ', + ], + // Serbian - Latin + 'sr__lat' => [ + '=' => ' jednak ', + '%' => ' procenat ', + '∑' => ' zbir ', + '∆' => ' delta ', + '∞' => ' beskraj ', + '♥' => ' lubav ', + '&' => ' i ', + '+' => ' vise ', + ], + // Azerbaijani + 'az' => [ + '=' => ' b@rab@r ', + '%' => ' faiz ', + '∑' => ' m@bl@g ', + '∆' => ' delta ', + '∞' => ' sonsuzluq ', + '♥' => ' sevgi ', + '&' => ' v@ ', + '+' => ' plus ', + ], + // Slovak + 'sk' => [ + '=' => ' rovny ', + '%' => ' percento ', + '∑' => ' sucet ', + '∆' => ' delta ', + '∞' => ' infinity ', + '♥' => ' milovat ', + '&' => ' a ', + '+' => ' viac ', + ], + // French + 'fr' => [ + '=' => ' Egal ', + '%' => ' Pourcentage ', + '∑' => ' somme ', + '∆' => ' delta ', + '∞' => ' infini ', + '♥' => ' amour ', + '&' => ' Et ', + '+' => ' Plus ', + ], + // Austrian (French) + 'fr_at' => [ + '=' => ' Egal ', + '%' => ' Pourcentage ', + '∑' => ' somme ', + '∆' => ' delta ', + '∞' => ' infini ', + '♥' => ' amour ', + '&' => ' Et ', + '+' => ' Plus ', + ], + // Switzerland (French) + 'fr_ch' => [ + '=' => ' Egal ', + '%' => ' Pourcentage ', + '∑' => ' somme ', + '∆' => ' delta ', + '∞' => ' infini ', + '♥' => ' amour ', + '&' => ' Et ', + '+' => ' Plus ', + ], + // German + 'de' => [ + '=' => ' gleich ', + '%' => ' Prozent ', + '∑' => ' gesamt ', + '∆' => ' Unterschied ', + '∞' => ' undendlich ', + '♥' => ' liebe ', + '&' => ' und ', + '+' => ' plus ', + ], + // Austrian (German) + 'de_at' => [ + '=' => ' gleich ', + '%' => ' Prozent ', + '∑' => ' gesamt ', + '∆' => ' Unterschied ', + '∞' => ' undendlich ', + '♥' => ' liebe ', + '&' => ' und ', + '+' => ' plus ', + ], + // Switzerland (German) + 'de_ch' => [ + '=' => ' gleich ', + '%' => ' Prozent ', + '∑' => ' gesamt ', + '∆' => ' Unterschied ', + '∞' => ' undendlich ', + '♥' => ' liebe ', + '&' => ' und ', + '+' => ' plus ', + ], + // Bengali (Bangla) + 'bn' => [ + '=' => ' Saman ', + '%' => ' Satakora ', + '∑' => ' Samasti ', + '∆' => ' Badhip ', + '∞' => ' Ananta ', + '♥' => ' Valobasa ', + '&' => ' Abong ', + '+' => ' Songzojon ', + ], + // English + 'en' => [ + '=' => ' equal ', + '%' => ' percent ', + '∑' => ' sum ', + '∆' => ' delta ', + '∞' => ' infinity ', + '♥' => ' love ', + '&' => ' and ', + '+' => ' plus ', + ], + // Currency + // + // url: https://en.wikipedia.org/wiki/Currency_symbol + 'currency' => [ + '€' => ' Euro ', + '$' => ' Dollar ', + '₢' => ' cruzeiro ', + '₣' => ' French franc ', + '£' => ' pound ', + '₤' => ' lira ', // Italian + '₶' => ' livre tournois ', + 'ℳ' => ' mark ', + '₥' => ' mill ', + '₦' => ' naira ', + '₧' => ' peseta ', + '₨' => ' rupee ', + 'රු' => ' rupee ', // Sri Lankan + 'ரூ' => ' rupee ', // Sri Lankan + '௹' => ' rupee ', // Tamil + 'रू' => ' rupee ', // Nepalese + '₹' => ' rupee ', // Indian + '૱' => ' rupee ', // Gujarat + '₩' => ' won ', + '₪' => ' new shequel ', + '₸' => ' tenge ', + '₫' => ' dong ', + '֏' => ' dram ', + '₭' => ' kip ', + '₺' => ' lira ', // Turkish + '₼' => ' manat ', + '₮' => ' tugrik ', + '₯' => ' drachma ', + '₰' => ' pfennig ', + '₷' => ' spesmilo ', + '₱' => ' peso ', // Philippine + '﷼‎' => ' riyal ', + '₲' => ' guarani ', + '₾' => ' lari ', + '₳' => ' austral ', + '₴' => ' hryvnia ', + '₽' => ' ruble ', + '₵' => ' cedi ', + '₡' => ' colon ', + '¢' => ' cent ', + '¥' => ' yen ', + '円' => ' yen ', + '৳' => ' taka ', + '元' => ' yuan ', + '﷼' => ' riyal ', + '៛' => ' riel ', + '₠' => ' European Currency ', + '¤' => ' currency ', + '฿' => ' baht ', + '؋' => ' afghani ', + ], + // Temperature + // + // url: https://en.wikipedia.org/wiki/Conversion_of_units_of_temperature + 'temperature' => [ + '°De' => ' Delisle ', + '°Re' => ' Reaumur ', // Réaumur + '°Ro' => ' Romer ', // Rømer + '°R' => ' Rankine ', + '°C' => ' Celsius ', + '°F' => ' Fahrenheit ', + '°N' => ' Newton ', + ], + 'latin_symbols' => [ + '=' => '=', + '%' => '%', + '∑' => '∑', + '∆' => '∆', + '∞' => '∞', + '♥' => '♥', + '&' => '&', + '+' => '+', + // --- + '©' => ' (c) ', + '®' => ' (r) ', + '@' => ' (at) ', + '№' => ' No. ', + '℞' => ' Rx ', + '[' => '[', + '\' => '\\', + ']' => ']', + '^' => '^', + '_' => '_', + '`' => '`', + '‐' => '-', + '‑' => '-', + '‒' => '-', + '–' => '-', + '−' => '-', + '—' => '-', + '―' => '-', + '﹘' => '-', + '│' => '|', + '∖' => '\\', + '∕' => '/', + '⁄' => '/', + '←' => '<-', + '→' => '->', + '↑' => '|', + '↓' => '|', + '⁅' => '[', + '⁆' => ']', + '⁎' => '*', + '、' => ',', + '。' => '.', + '〈' => '<', + '〉' => '>', + '《' => '<<', + '》' => '>>', + '〔' => '[', + '〕' => ']', + '〘' => '[', + '〙' => ']', + '〚' => '[', + '〛' => ']', + '﹝' => '[', + '﹞' => ']', + '︹' => '[', + '︺' => ']', + '﹇' => '[', + '﹈' => ']', + '︐' => ',', + '︑' => ',', + '︒' => '.', + '︓' => ':', + '︔' => ';', + '︕' => '!', + '︖' => '?', + '︙' => '...', + '︰' => '..', + '︵' => '(', + '︶' => ')', + '﹙' => '(', + '﹚' => ')', + '︷' => '{', + '︸' => '}', + '﹛' => '{', + '﹜' => '}', + '︽' => '<<', + '︾' => '>>', + '︿' => '<', + '﹀' => '>', + '×' => '*', + '÷' => '/', + '≪' => '<<', + '≫' => '>>', + '⦅' => '((', + '⦆' => '))', + '〇' => '0', + '′' => '\'', + '〝' => '"', + '〞' => '"', + '«' => '<<', + '»' => '>>', + '‘' => "'", + '’' => "'", + '‚' => ',', + '‛' => "'", + '“' => '"', + '”' => '"', + '„' => '"', + '‟' => '"', + '‹' => '<', + '›' => '>', + '․' => '.', + '‥' => '..', + '…' => '...', + '″' => '"', + '‴' => '\'\'\'', + '‶' => '``', + '‷' => '```', + '‼' => '!!', + '⁇' => '??', + '⁈' => '?!', + '⁉' => '!?', + '⁗' => '````', + '⩴' => '::=', + '⩵' => '==', + '⩶' => '===', + '﹔' => ';', + '﹕' => ':', + '﹖' => '?', + '﹗' => '!', + '﹍' => '_', + '﹎' => '_', + '﹏' => '_', + '﹐' => ',', + '﹑' => ',', + '﹒' => '.', + '﹟' => '#', + '﹠' => '&', + '﹡' => '*', + '﹢' => '+', + '﹣' => '-', + '﹤' => '<', + '﹥' => '>', + '﹦' => '=', + '﹨' => '\\', + '﹩' => '$', + '﹪' => '%', + '﹫' => '@', + '!' => '!', + '"' => '"', + '#' => '#', + '$' => '$', + '%' => '%', + '&' => '&', + ''' => '\'', + '(' => '(', + ')' => ')', + '*' => '*', + '+' => '+', + ',' => ',', + '-' => '-', + '.' => '.', + '/' => '/', + ':' => ':', + ';' => ';', + '<' => '<', + '=' => '=', + '>' => '>', + '?' => '?', + '@' => '@', + '{' => '{', + '|' => '|', + '}' => '}', + '~' => '~', + '⦅' => '((', + '⦆' => '))', + '¬' => '!', + ' ̄' => '-', + '¦' => '|', + '■' => '#', + ], +]; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/ascii_language_max_key.php b/includes/libraries/anti-xss-master/src/voku/helper/data/ascii_language_max_key.php new file mode 100644 index 000000000..da81ae236 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/ascii_language_max_key.php @@ -0,0 +1,65 @@ + 0, + 'tk' => 1, + 'th' => 0, + 'ps' => 0, + 'or' => 0, + 'mn' => 0, + 'ko' => 0, + 'ky' => 0, + 'hy' => 1, + 'bn' => 5, + 'be' => 0, + 'am' => 0, + 'ja' => 0, + 'zh' => 0, + 'nl' => 1, + 'it' => 1, + 'mk' => 1, + 'pt' => 1, + 'el__greeklish' => 2, + 'el' => 2, + 'hi' => 2, + 'sv' => 1, + 'tr' => 1, + 'bg' => 2, + 'hu' => 1, + 'my' => 5, + 'hr' => 2, + 'fi' => 1, + 'ka' => 1, + 'ru' => 1, + 'ru__gost_2000_b' => 1, + 'ru__passport_2013' => 1, + 'uk' => 1, + 'kk' => 1, + 'cs' => 1, + 'da' => 1, + 'pl' => 1, + 'ro' => 1, + 'eo' => 1, + 'et' => 1, + 'lv' => 1, + 'lt' => 1, + 'no' => 1, + 'vi' => 1, + 'ar' => 1, + 'fa' => 1, + 'sr' => 1, + 'sr__cyr' => 1, + 'sr__lat' => 1, + 'az' => 1, + 'sk' => 1, + 'fr' => 1, + 'fr_at' => 1, + 'fr_ch' => 1, + 'de' => 1, + 'de_at' => 1, + 'de_ch' => 1, + 'en' => 0, + 'latin' => 3, + ' ' => 1, + 'msword' => 1, +]; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/ascii_ord.php b/includes/libraries/anti-xss-master/src/voku/helper/data/ascii_ord.php new file mode 100644 index 000000000..142318c33 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/ascii_ord.php @@ -0,0 +1 @@ + 0, "\x00" => 0, "\x01" => 1, "\x02" => 2, "\x03" => 3, "\x04" => 4, "\x05" => 5, "\x06" => 6, "\x07" => 7, "\x08" => 8, "\x09" => 9, "\x0A" => 10, "\x0B" => 11, "\x0C" => 12, "\x0D" => 13, "\x0E" => 14, "\x0F" => 15, "\x10" => 16, "\x11" => 17, "\x12" => 18, "\x13" => 19, "\x14" => 20, "\x15" => 21, "\x16" => 22, "\x17" => 23, "\x18" => 24, "\x19" => 25, "\x1A" => 26, "\x1B" => 27, "\x1C" => 28, "\x1D" => 29, "\x1E" => 30, "\x1F" => 31, "\x20" => 32, "\x21" => 33, "\x22" => 34, "\x23" => 35, "\x24" => 36, "\x25" => 37, "\x26" => 38, "\x27" => 39, "\x28" => 40, "\x29" => 41, "\x2A" => 42, "\x2B" => 43, "\x2C" => 44, "\x2D" => 45, "\x2E" => 46, "\x2F" => 47, "\x30" => 48, "\x31" => 49, "\x32" => 50, "\x33" => 51, "\x34" => 52, "\x35" => 53, "\x36" => 54, "\x37" => 55, "\x38" => 56, "\x39" => 57, "\x3A" => 58, "\x3B" => 59, "\x3C" => 60, "\x3D" => 61, "\x3E" => 62, "\x3F" => 63, "\x40" => 64, "\x41" => 65, "\x42" => 66, "\x43" => 67, "\x44" => 68, "\x45" => 69, "\x46" => 70, "\x47" => 71, "\x48" => 72, "\x49" => 73, "\x4A" => 74, "\x4B" => 75, "\x4C" => 76, "\x4D" => 77, "\x4E" => 78, "\x4F" => 79, "\x50" => 80, "\x51" => 81, "\x52" => 82, "\x53" => 83, "\x54" => 84, "\x55" => 85, "\x56" => 86, "\x57" => 87, "\x58" => 88, "\x59" => 89, "\x5A" => 90, "\x5B" => 91, "\x5C" => 92, "\x5D" => 93, "\x5E" => 94, "\x5F" => 95, "\x60" => 96, "\x61" => 97, "\x62" => 98, "\x63" => 99, "\x64" => 100, "\x65" => 101, "\x66" => 102, "\x67" => 103, "\x68" => 104, "\x69" => 105, "\x6A" => 106, "\x6B" => 107, "\x6C" => 108, "\x6D" => 109, "\x6E" => 110, "\x6F" => 111, "\x70" => 112, "\x71" => 113, "\x72" => 114, "\x73" => 115, "\x74" => 116, "\x75" => 117, "\x76" => 118, "\x77" => 119, "\x78" => 120, "\x79" => 121, "\x7A" => 122, "\x7B" => 123, "\x7C" => 124, "\x7D" => 125, "\x7E" => 126, "\x7F" => 127, "\x80" => 128, "\x81" => 129, "\x82" => 130, "\x83" => 131, "\x84" => 132, "\x85" => 133, "\x86" => 134, "\x87" => 135, "\x88" => 136, "\x89" => 137, "\x8A" => 138, "\x8B" => 139, "\x8C" => 140, "\x8D" => 141, "\x8E" => 142, "\x8F" => 143, "\x90" => 144, "\x91" => 145, "\x92" => 146, "\x93" => 147, "\x94" => 148, "\x95" => 149, "\x96" => 150, "\x97" => 151, "\x98" => 152, "\x99" => 153, "\x9A" => 154, "\x9B" => 155, "\x9C" => 156, "\x9D" => 157, "\x9E" => 158, "\x9F" => 159, "\xA0" => 160, "\xA1" => 161, "\xA2" => 162, "\xA3" => 163, "\xA4" => 164, "\xA5" => 165, "\xA6" => 166, "\xA7" => 167, "\xA8" => 168, "\xA9" => 169, "\xAA" => 170, "\xAB" => 171, "\xAC" => 172, "\xAD" => 173, "\xAE" => 174, "\xAF" => 175, "\xB0" => 176, "\xB1" => 177, "\xB2" => 178, "\xB3" => 179, "\xB4" => 180, "\xB5" => 181, "\xB6" => 182, "\xB7" => 183, "\xB8" => 184, "\xB9" => 185, "\xBA" => 186, "\xBB" => 187, "\xBC" => 188, "\xBD" => 189, "\xBE" => 190, "\xBF" => 191, "\xC0" => 192, "\xC1" => 193, "\xC2" => 194, "\xC3" => 195, "\xC4" => 196, "\xC5" => 197, "\xC6" => 198, "\xC7" => 199, "\xC8" => 200, "\xC9" => 201, "\xCA" => 202, "\xCB" => 203, "\xCC" => 204, "\xCD" => 205, "\xCE" => 206, "\xCF" => 207, "\xD0" => 208, "\xD1" => 209, "\xD2" => 210, "\xD3" => 211, "\xD4" => 212, "\xD5" => 213, "\xD6" => 214, "\xD7" => 215, "\xD8" => 216, "\xD9" => 217, "\xDA" => 218, "\xDB" => 219, "\xDC" => 220, "\xDD" => 221, "\xDE" => 222, "\xDF" => 223, "\xE0" => 224, "\xE1" => 225, "\xE2" => 226, "\xE3" => 227, "\xE4" => 228, "\xE5" => 229, "\xE6" => 230, "\xE7" => 231, "\xE8" => 232, "\xE9" => 233, "\xEA" => 234, "\xEB" => 235, "\xEC" => 236, "\xED" => 237, "\xEE" => 238, "\xEF" => 239, "\xF0" => 240, "\xF1" => 241, "\xF2" => 242, "\xF3" => 243, "\xF4" => 244, "\xF5" => 245, "\xF6" => 246, "\xF7" => 247, "\xF8" => 248, "\xF9" => 249, "\xFA" => 250, "\xFB" => 251, "\xFC" => 252, "\xFD" => 253, "\xFE" => 254, "\xFF" => 255]; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/caseFolding_full.php b/includes/libraries/anti-xss-master/src/voku/helper/data/caseFolding_full.php new file mode 100644 index 000000000..258104597 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/caseFolding_full.php @@ -0,0 +1,218 @@ + [ + 0 => 'ß', + 1 => 'İ', + 2 => 'ʼn', + 3 => 'ǰ', + 4 => 'ΐ', + 5 => 'ΰ', + 6 => 'և', + 7 => 'ẖ', + 8 => 'ẗ', + 9 => 'ẘ', + 10 => 'ẙ', + 11 => 'ẚ', + 12 => 'ẞ', + 13 => 'ὐ', + 14 => 'ὒ', + 15 => 'ὔ', + 16 => 'ὖ', + 17 => 'ᾀ', + 18 => 'ᾁ', + 19 => 'ᾂ', + 20 => 'ᾃ', + 21 => 'ᾄ', + 22 => 'ᾅ', + 23 => 'ᾆ', + 24 => 'ᾇ', + 25 => 'ᾈ', + 26 => 'ᾉ', + 27 => 'ᾊ', + 28 => 'ᾋ', + 29 => 'ᾌ', + 30 => 'ᾍ', + 31 => 'ᾎ', + 32 => 'ᾏ', + 33 => 'ᾐ', + 34 => 'ᾑ', + 35 => 'ᾒ', + 36 => 'ᾓ', + 37 => 'ᾔ', + 38 => 'ᾕ', + 39 => 'ᾖ', + 40 => 'ᾗ', + 41 => 'ᾘ', + 42 => 'ᾙ', + 43 => 'ᾚ', + 44 => 'ᾛ', + 45 => 'ᾜ', + 46 => 'ᾝ', + 47 => 'ᾞ', + 48 => 'ᾟ', + 49 => 'ᾠ', + 50 => 'ᾡ', + 51 => 'ᾢ', + 52 => 'ᾣ', + 53 => 'ᾤ', + 54 => 'ᾥ', + 55 => 'ᾦ', + 56 => 'ᾧ', + 57 => 'ᾨ', + 58 => 'ᾩ', + 59 => 'ᾪ', + 60 => 'ᾫ', + 61 => 'ᾬ', + 62 => 'ᾭ', + 63 => 'ᾮ', + 64 => 'ᾯ', + 65 => 'ᾲ', + 66 => 'ᾳ', + 67 => 'ᾴ', + 68 => 'ᾶ', + 69 => 'ᾷ', + 70 => 'ᾼ', + 71 => 'ῂ', + 72 => 'ῃ', + 73 => 'ῄ', + 74 => 'ῆ', + 75 => 'ῇ', + 76 => 'ῌ', + 77 => 'ῒ', + 78 => 'ΐ', + 79 => 'ῖ', + 80 => 'ῗ', + 81 => 'ῢ', + 82 => 'ΰ', + 83 => 'ῤ', + 84 => 'ῦ', + 85 => 'ῧ', + 86 => 'ῲ', + 87 => 'ῳ', + 88 => 'ῴ', + 89 => 'ῶ', + 90 => 'ῷ', + 91 => 'ῼ', + 92 => 'ff', + 93 => 'fi', + 94 => 'fl', + 95 => 'ffi', + 96 => 'ffl', + 97 => 'ſt', + 98 => 'st', + 99 => 'ﬓ', + 100 => 'ﬔ', + 101 => 'ﬕ', + 102 => 'ﬖ', + 103 => 'ﬗ', + 104 => 'J̌̌', + ], + 1 => [ + 0 => 'ss', + 1 => 'i̇', + 2 => 'ʼn', + 3 => 'ǰ', + 4 => 'ΐ', + 5 => 'ΰ', + 6 => 'եւ', + 7 => 'ẖ', + 8 => 'ẗ', + 9 => 'ẘ', + 10 => 'ẙ', + 11 => 'aʾ', + 12 => 'ss', + 13 => 'ὐ', + 14 => 'ὒ', + 15 => 'ὔ', + 16 => 'ὖ', + 17 => 'ἀι', + 18 => 'ἁι', + 19 => 'ἂι', + 20 => 'ἃι', + 21 => 'ἄι', + 22 => 'ἅι', + 23 => 'ἆι', + 24 => 'ἇι', + 25 => 'ἀι', + 26 => 'ἁι', + 27 => 'ἂι', + 28 => 'ἃι', + 29 => 'ἄι', + 30 => 'ἅι', + 31 => 'ἆι', + 32 => 'ἇι', + 33 => 'ἠι', + 34 => 'ἡι', + 35 => 'ἢι', + 36 => 'ἣι', + 37 => 'ἤι', + 38 => 'ἥι', + 39 => 'ἦι', + 40 => 'ἧι', + 41 => 'ἠι', + 42 => 'ἡι', + 43 => 'ἢι', + 44 => 'ἣι', + 45 => 'ἤι', + 46 => 'ἥι', + 47 => 'ἦι', + 48 => 'ἧι', + 49 => 'ὠι', + 50 => 'ὡι', + 51 => 'ὢι', + 52 => 'ὣι', + 53 => 'ὤι', + 54 => 'ὥι', + 55 => 'ὦι', + 56 => 'ὧι', + 57 => 'ὠι', + 58 => 'ὡι', + 59 => 'ὢι', + 60 => 'ὣι', + 61 => 'ὤι', + 62 => 'ὥι', + 63 => 'ὦι', + 64 => 'ὧι', + 65 => 'ὰι', + 66 => 'αι', + 67 => 'άι', + 68 => 'ᾶ', + 69 => 'ᾶι', + 70 => 'αι', + 71 => 'ὴι', + 72 => 'ηι', + 73 => 'ήι', + 74 => 'ῆ', + 75 => 'ῆι', + 76 => 'ηι', + 77 => 'ῒ', + 78 => 'ΐ', + 79 => 'ῖ', + 80 => 'ῗ', + 81 => 'ῢ', + 82 => 'ΰ', + 83 => 'ῤ', + 84 => 'ῦ', + 85 => 'ῧ', + 86 => 'ὼι', + 87 => 'ωι', + 88 => 'ώι', + 89 => 'ῶ', + 90 => 'ῶι', + 91 => 'ωι', + 92 => 'ff', + 93 => 'fi', + 94 => 'fl', + 95 => 'ffi', + 96 => 'ffl', + 97 => 'st', + 98 => 'st', + 99 => 'մն', + 100 => 'մե', + 101 => 'մի', + 102 => 'վն', + 103 => 'մխ', + 104 => 'ǰ', + ], +]; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/chr.php b/includes/libraries/anti-xss-master/src/voku/helper/data/chr.php new file mode 100644 index 000000000..e20ffa647 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/chr.php @@ -0,0 +1 @@ + "\x00", 1 => "\x01", 2 => "\x02", 3 => "\x03", 4 => "\x04", 5 => "\x05", 6 => "\x06", 7 => "\x07", 8 => "\x08", 9 => "\x09", 10 => "\x0A", 11 => "\x0B", 12 => "\x0C", 13 => "\x0D", 14 => "\x0E", 15 => "\x0F", 16 => "\x10", 17 => "\x11", 18 => "\x12", 19 => "\x13", 20 => "\x14", 21 => "\x15", 22 => "\x16", 23 => "\x17", 24 => "\x18", 25 => "\x19", 26 => "\x1A", 27 => "\x1B", 28 => "\x1C", 29 => "\x1D", 30 => "\x1E", 31 => "\x1F", 32 => "\x20", 33 => "\x21", 34 => "\x22", 35 => "\x23", 36 => "\x24", 37 => "\x25", 38 => "\x26", 39 => "\x27", 40 => "\x28", 41 => "\x29", 42 => "\x2A", 43 => "\x2B", 44 => "\x2C", 45 => "\x2D", 46 => "\x2E", 47 => "\x2F", 48 => "\x30", 49 => "\x31", 50 => "\x32", 51 => "\x33", 52 => "\x34", 53 => "\x35", 54 => "\x36", 55 => "\x37", 56 => "\x38", 57 => "\x39", 58 => "\x3A", 59 => "\x3B", 60 => "\x3C", 61 => "\x3D", 62 => "\x3E", 63 => "\x3F", 64 => "\x40", 65 => "\x41", 66 => "\x42", 67 => "\x43", 68 => "\x44", 69 => "\x45", 70 => "\x46", 71 => "\x47", 72 => "\x48", 73 => "\x49", 74 => "\x4A", 75 => "\x4B", 76 => "\x4C", 77 => "\x4D", 78 => "\x4E", 79 => "\x4F", 80 => "\x50", 81 => "\x51", 82 => "\x52", 83 => "\x53", 84 => "\x54", 85 => "\x55", 86 => "\x56", 87 => "\x57", 88 => "\x58", 89 => "\x59", 90 => "\x5A", 91 => "\x5B", 92 => "\x5C", 93 => "\x5D", 94 => "\x5E", 95 => "\x5F", 96 => "\x60", 97 => "\x61", 98 => "\x62", 99 => "\x63", 100 => "\x64", 101 => "\x65", 102 => "\x66", 103 => "\x67", 104 => "\x68", 105 => "\x69", 106 => "\x6A", 107 => "\x6B", 108 => "\x6C", 109 => "\x6D", 110 => "\x6E", 111 => "\x6F", 112 => "\x70", 113 => "\x71", 114 => "\x72", 115 => "\x73", 116 => "\x74", 117 => "\x75", 118 => "\x76", 119 => "\x77", 120 => "\x78", 121 => "\x79", 122 => "\x7A", 123 => "\x7B", 124 => "\x7C", 125 => "\x7D", 126 => "\x7E", 127 => "\x7F", 128 => "\x80", 129 => "\x81", 130 => "\x82", 131 => "\x83", 132 => "\x84", 133 => "\x85", 134 => "\x86", 135 => "\x87", 136 => "\x88", 137 => "\x89", 138 => "\x8A", 139 => "\x8B", 140 => "\x8C", 141 => "\x8D", 142 => "\x8E", 143 => "\x8F", 144 => "\x90", 145 => "\x91", 146 => "\x92", 147 => "\x93", 148 => "\x94", 149 => "\x95", 150 => "\x96", 151 => "\x97", 152 => "\x98", 153 => "\x99", 154 => "\x9A", 155 => "\x9B", 156 => "\x9C", 157 => "\x9D", 158 => "\x9E", 159 => "\x9F", 160 => "\xA0", 161 => "\xA1", 162 => "\xA2", 163 => "\xA3", 164 => "\xA4", 165 => "\xA5", 166 => "\xA6", 167 => "\xA7", 168 => "\xA8", 169 => "\xA9", 170 => "\xAA", 171 => "\xAB", 172 => "\xAC", 173 => "\xAD", 174 => "\xAE", 175 => "\xAF", 176 => "\xB0", 177 => "\xB1", 178 => "\xB2", 179 => "\xB3", 180 => "\xB4", 181 => "\xB5", 182 => "\xB6", 183 => "\xB7", 184 => "\xB8", 185 => "\xB9", 186 => "\xBA", 187 => "\xBB", 188 => "\xBC", 189 => "\xBD", 190 => "\xBE", 191 => "\xBF", 192 => "\xC0", 193 => "\xC1", 194 => "\xC2", 195 => "\xC3", 196 => "\xC4", 197 => "\xC5", 198 => "\xC6", 199 => "\xC7", 200 => "\xC8", 201 => "\xC9", 202 => "\xCA", 203 => "\xCB", 204 => "\xCC", 205 => "\xCD", 206 => "\xCE", 207 => "\xCF", 208 => "\xD0", 209 => "\xD1", 210 => "\xD2", 211 => "\xD3", 212 => "\xD4", 213 => "\xD5", 214 => "\xD6", 215 => "\xD7", 216 => "\xD8", 217 => "\xD9", 218 => "\xDA", 219 => "\xDB", 220 => "\xDC", 221 => "\xDD", 222 => "\xDE", 223 => "\xDF", 224 => "\xE0", 225 => "\xE1", 226 => "\xE2", 227 => "\xE3", 228 => "\xE4", 229 => "\xE5", 230 => "\xE6", 231 => "\xE7", 232 => "\xE8", 233 => "\xE9", 234 => "\xEA", 235 => "\xEB", 236 => "\xEC", 237 => "\xED", 238 => "\xEE", 239 => "\xEF", 240 => "\xF0", 241 => "\xF1", 242 => "\xF2", 243 => "\xF3", 244 => "\xF4", 245 => "\xF5", 246 => "\xF6", 247 => "\xF7", 248 => "\xF8", 249 => "\xF9", 250 => "\xFA", 251 => "\xFB", 252 => "\xFC", 253 => "\xFD", 254 => "\xFE", 255 => "\xFF"]; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/emoji.php b/includes/libraries/anti-xss-master/src/voku/helper/data/emoji.php new file mode 100644 index 000000000..ae10b33ef --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/emoji.php @@ -0,0 +1,7 @@ + "\u{1F600}", 'CHARACTER_BEAMING_FACE_WITH_SMILING_EYES' => "\u{1F601}", 'CHARACTER_FACE_WITH_TEARS_OF_JOY' => "\u{1F602}", 'CHARACTER_ROLLING_ON_THE_FLOOR_LAUGHING' => "\u{1F923}", 'CHARACTER_GRINNING_FACE_WITH_BIG_EYES' => "\u{1F603}", 'CHARACTER_GRINNING_FACE_WITH_SMILING_EYES' => "\u{1F604}", 'CHARACTER_GRINNING_FACE_WITH_SWEAT' => "\u{1F605}", 'CHARACTER_GRINNING_SQUINTING_FACE' => "\u{1F606}", 'CHARACTER_WINKING_FACE' => "\u{1F609}", 'CHARACTER_SMILING_FACE_WITH_SMILING_EYES' => "\u{1F60A}", 'CHARACTER_FACE_SAVORING_FOOD' => "\u{1F60B}", 'CHARACTER_SMILING_FACE_WITH_SUNGLASSES' => "\u{1F60E}", 'CHARACTER_SMILING_FACE_WITH_HEART_EYES' => "\u{1F60D}", 'CHARACTER_FACE_BLOWING_A_KISS' => "\u{1F618}", 'CHARACTER_SMILING_FACE_WITH_3_HEARTS' => "\u{1F970}", 'CHARACTER_KISSING_FACE' => "\u{1F617}", 'CHARACTER_KISSING_FACE_WITH_SMILING_EYES' => "\u{1F619}", 'CHARACTER_KISSING_FACE_WITH_CLOSED_EYES' => "\u{1F61A}", 'CHARACTER_SMILING_FACE' => "\u{263A}\u{FE0F}", 'CHARACTER_SLIGHTLY_SMILING_FACE' => "\u{1F642}", 'CHARACTER_HUGGING_FACE' => "\u{1F917}", 'CHARACTER_STAR_STRUCK' => "\u{1F929}", 'CHARACTER_THINKING_FACE' => "\u{1F914}", 'CHARACTER_FACE_WITH_RAISED_EYEBROW' => "\u{1F928}", 'CHARACTER_NEUTRAL_FACE' => "\u{1F610}", 'CHARACTER_EXPRESSIONLESS_FACE' => "\u{1F611}", 'CHARACTER_FACE_WITHOUT_MOUTH' => "\u{1F636}", 'CHARACTER_FACE_WITH_ROLLING_EYES' => "\u{1F644}", 'CHARACTER_SMIRKING_FACE' => "\u{1F60F}", 'CHARACTER_PERSEVERING_FACE' => "\u{1F623}", 'CHARACTER_SAD_BUT_RELIEVED_FACE' => "\u{1F625}", 'CHARACTER_FACE_WITH_OPEN_MOUTH' => "\u{1F62E}", 'CHARACTER_ZIPPER_MOUTH_FACE' => "\u{1F910}", 'CHARACTER_HUSHED_FACE' => "\u{1F62F}", 'CHARACTER_SLEEPY_FACE' => "\u{1F62A}", 'CHARACTER_TIRED_FACE' => "\u{1F62B}", 'CHARACTER_SLEEPING_FACE' => "\u{1F634}", 'CHARACTER_RELIEVED_FACE' => "\u{1F60C}", 'CHARACTER_FACE_WITH_TONGUE' => "\u{1F61B}", 'CHARACTER_WINKING_FACE_WITH_TONGUE' => "\u{1F61C}", 'CHARACTER_SQUINTING_FACE_WITH_TONGUE' => "\u{1F61D}", 'CHARACTER_DROOLING_FACE' => "\u{1F924}", 'CHARACTER_UNAMUSED_FACE' => "\u{1F612}", 'CHARACTER_DOWNCAST_FACE_WITH_SWEAT' => "\u{1F613}", 'CHARACTER_PENSIVE_FACE' => "\u{1F614}", 'CHARACTER_CONFUSED_FACE' => "\u{1F615}", 'CHARACTER_UPSIDE_DOWN_FACE' => "\u{1F643}", 'CHARACTER_MONEY_MOUTH_FACE' => "\u{1F911}", 'CHARACTER_ASTONISHED_FACE' => "\u{1F632}", 'CHARACTER_FROWNING_FACE' => "\u{2639}\u{FE0F}", 'CHARACTER_SLIGHTLY_FROWNING_FACE' => "\u{1F641}", 'CHARACTER_CONFOUNDED_FACE' => "\u{1F616}", 'CHARACTER_DISAPPOINTED_FACE' => "\u{1F61E}", 'CHARACTER_WORRIED_FACE' => "\u{1F61F}", 'CHARACTER_FACE_WITH_STEAM_FROM_NOSE' => "\u{1F624}", 'CHARACTER_CRYING_FACE' => "\u{1F622}", 'CHARACTER_LOUDLY_CRYING_FACE' => "\u{1F62D}", 'CHARACTER_FROWNING_FACE_WITH_OPEN_MOUTH' => "\u{1F626}", 'CHARACTER_ANGUISHED_FACE' => "\u{1F627}", 'CHARACTER_FEARFUL_FACE' => "\u{1F628}", 'CHARACTER_WEARY_FACE' => "\u{1F629}", 'CHARACTER_EXPLODING_HEAD' => "\u{1F92F}", 'CHARACTER_GRIMACING_FACE' => "\u{1F62C}", 'CHARACTER_ANXIOUS_FACE_WITH_SWEAT' => "\u{1F630}", 'CHARACTER_FACE_SCREAMING_IN_FEAR' => "\u{1F631}", 'CHARACTER_HOT_FACE' => "\u{1F975}", 'CHARACTER_COLD_FACE' => "\u{1F976}", 'CHARACTER_FLUSHED_FACE' => "\u{1F633}", 'CHARACTER_ZANY_FACE' => "\u{1F92A}", 'CHARACTER_DIZZY_FACE' => "\u{1F635}", 'CHARACTER_POUTING_FACE' => "\u{1F621}", 'CHARACTER_ANGRY_FACE' => "\u{1F620}", 'CHARACTER_FACE_WITH_SYMBOLS_ON_MOUTH' => "\u{1F92C}", 'CHARACTER_FACE_WITH_MEDICAL_MASK' => "\u{1F637}", 'CHARACTER_FACE_WITH_THERMOMETER' => "\u{1F912}", 'CHARACTER_FACE_WITH_HEAD_BANDAGE' => "\u{1F915}", 'CHARACTER_NAUSEATED_FACE' => "\u{1F922}", 'CHARACTER_FACE_VOMITING' => "\u{1F92E}", 'CHARACTER_SNEEZING_FACE' => "\u{1F927}", 'CHARACTER_SMILING_FACE_WITH_HALO' => "\u{1F607}", 'CHARACTER_COWBOY_HAT_FACE' => "\u{1F920}", 'CHARACTER_PARTYING_FACE' => "\u{1F973}", 'CHARACTER_WOOZY_FACE' => "\u{1F974}", 'CHARACTER_PLEADING_FACE' => "\u{1F97A}", 'CHARACTER_LYING_FACE' => "\u{1F925}", 'CHARACTER_SHUSHING_FACE' => "\u{1F92B}", 'CHARACTER_FACE_WITH_HAND_OVER_MOUTH' => "\u{1F92D}", 'CHARACTER_FACE_WITH_MONOCLE' => "\u{1F9D0}", 'CHARACTER_NERD_FACE' => "\u{1F913}", 'CHARACTER_SMILING_FACE_WITH_HORNS' => "\u{1F608}", 'CHARACTER_ANGRY_FACE_WITH_HORNS' => "\u{1F47F}", 'CHARACTER_CLOWN_FACE' => "\u{1F921}", 'CHARACTER_OGRE' => "\u{1F479}", 'CHARACTER_GOBLIN' => "\u{1F47A}", 'CHARACTER_SKULL' => "\u{1F480}", 'CHARACTER_SKULL_AND_CROSSBONES' => "\u{2620}\u{FE0F}", 'CHARACTER_GHOST' => "\u{1F47B}", 'CHARACTER_ALIEN' => "\u{1F47D}", 'CHARACTER_ALIEN_MONSTER' => "\u{1F47E}", 'CHARACTER_ROBOT_FACE' => "\u{1F916}", 'CHARACTER_PILE_OF_POO' => "\u{1F4A9}", 'CHARACTER_GRINNING_CAT_FACE' => "\u{1F63A}", 'CHARACTER_GRINNING_CAT_FACE_WITH_SMILING_EYES' => "\u{1F638}", 'CHARACTER_CAT_FACE_WITH_TEARS_OF_JOY' => "\u{1F639}", 'CHARACTER_SMILING_CAT_FACE_WITH_HEART_EYES' => "\u{1F63B}", 'CHARACTER_CAT_FACE_WITH_WRY_SMILE' => "\u{1F63C}", 'CHARACTER_KISSING_CAT_FACE' => "\u{1F63D}", 'CHARACTER_WEARY_CAT_FACE' => "\u{1F640}", 'CHARACTER_CRYING_CAT_FACE' => "\u{1F63F}", 'CHARACTER_POUTING_CAT_FACE' => "\u{1F63E}", 'CHARACTER_SEE_NO_EVIL_MONKEY' => "\u{1F648}", 'CHARACTER_HEAR_NO_EVIL_MONKEY' => "\u{1F649}", 'CHARACTER_SPEAK_NO_EVIL_MONKEY' => "\u{1F64A}", 'CHARACTER_LIGHT_SKIN_TONE' => "\u{1F3FB}", 'CHARACTER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F3FC}", 'CHARACTER_MEDIUM_SKIN_TONE' => "\u{1F3FD}", 'CHARACTER_MEDIUM_DARK_SKIN_TONE' => "\u{1F3FE}", 'CHARACTER_DARK_SKIN_TONE' => "\u{1F3FF}", 'CHARACTER_BABY' => "\u{1F476}", 'CHARACTER_BABY_LIGHT_SKIN_TONE' => "\u{1F476}\u{1F3FB}", 'CHARACTER_BABY_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F476}\u{1F3FC}", 'CHARACTER_BABY_MEDIUM_SKIN_TONE' => "\u{1F476}\u{1F3FD}", 'CHARACTER_BABY_MEDIUM_DARK_SKIN_TONE' => "\u{1F476}\u{1F3FE}", 'CHARACTER_BABY_DARK_SKIN_TONE' => "\u{1F476}\u{1F3FF}", 'CHARACTER_CHILD' => "\u{1F9D2}", 'CHARACTER_CHILD_LIGHT_SKIN_TONE' => "\u{1F9D2}\u{1F3FB}", 'CHARACTER_CHILD_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9D2}\u{1F3FC}", 'CHARACTER_CHILD_MEDIUM_SKIN_TONE' => "\u{1F9D2}\u{1F3FD}", 'CHARACTER_CHILD_MEDIUM_DARK_SKIN_TONE' => "\u{1F9D2}\u{1F3FE}", 'CHARACTER_CHILD_DARK_SKIN_TONE' => "\u{1F9D2}\u{1F3FF}", 'CHARACTER_BOY' => "\u{1F466}", 'CHARACTER_BOY_LIGHT_SKIN_TONE' => "\u{1F466}\u{1F3FB}", 'CHARACTER_BOY_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F466}\u{1F3FC}", 'CHARACTER_BOY_MEDIUM_SKIN_TONE' => "\u{1F466}\u{1F3FD}", 'CHARACTER_BOY_MEDIUM_DARK_SKIN_TONE' => "\u{1F466}\u{1F3FE}", 'CHARACTER_BOY_DARK_SKIN_TONE' => "\u{1F466}\u{1F3FF}", 'CHARACTER_GIRL' => "\u{1F467}", 'CHARACTER_GIRL_LIGHT_SKIN_TONE' => "\u{1F467}\u{1F3FB}", 'CHARACTER_GIRL_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F467}\u{1F3FC}", 'CHARACTER_GIRL_MEDIUM_SKIN_TONE' => "\u{1F467}\u{1F3FD}", 'CHARACTER_GIRL_MEDIUM_DARK_SKIN_TONE' => "\u{1F467}\u{1F3FE}", 'CHARACTER_GIRL_DARK_SKIN_TONE' => "\u{1F467}\u{1F3FF}", 'CHARACTER_ADULT' => "\u{1F9D1}", 'CHARACTER_ADULT_LIGHT_SKIN_TONE' => "\u{1F9D1}\u{1F3FB}", 'CHARACTER_ADULT_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9D1}\u{1F3FC}", 'CHARACTER_ADULT_MEDIUM_SKIN_TONE' => "\u{1F9D1}\u{1F3FD}", 'CHARACTER_ADULT_MEDIUM_DARK_SKIN_TONE' => "\u{1F9D1}\u{1F3FE}", 'CHARACTER_ADULT_DARK_SKIN_TONE' => "\u{1F9D1}\u{1F3FF}", 'CHARACTER_MAN' => "\u{1F468}", 'CHARACTER_MAN_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}", 'CHARACTER_MAN_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}", 'CHARACTER_MAN_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}", 'CHARACTER_MAN_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}", 'CHARACTER_MAN_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}", 'CHARACTER_WOMAN' => "\u{1F469}", 'CHARACTER_WOMAN_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}", 'CHARACTER_WOMAN_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}", 'CHARACTER_WOMAN_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}", 'CHARACTER_WOMAN_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}", 'CHARACTER_WOMAN_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}", 'CHARACTER_OLDER_ADULT' => "\u{1F9D3}", 'CHARACTER_OLDER_ADULT_LIGHT_SKIN_TONE' => "\u{1F9D3}\u{1F3FB}", 'CHARACTER_OLDER_ADULT_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9D3}\u{1F3FC}", 'CHARACTER_OLDER_ADULT_MEDIUM_SKIN_TONE' => "\u{1F9D3}\u{1F3FD}", 'CHARACTER_OLDER_ADULT_MEDIUM_DARK_SKIN_TONE' => "\u{1F9D3}\u{1F3FE}", 'CHARACTER_OLDER_ADULT_DARK_SKIN_TONE' => "\u{1F9D3}\u{1F3FF}", 'CHARACTER_OLD_MAN' => "\u{1F474}", 'CHARACTER_OLD_MAN_LIGHT_SKIN_TONE' => "\u{1F474}\u{1F3FB}", 'CHARACTER_OLD_MAN_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F474}\u{1F3FC}", 'CHARACTER_OLD_MAN_MEDIUM_SKIN_TONE' => "\u{1F474}\u{1F3FD}", 'CHARACTER_OLD_MAN_MEDIUM_DARK_SKIN_TONE' => "\u{1F474}\u{1F3FE}", 'CHARACTER_OLD_MAN_DARK_SKIN_TONE' => "\u{1F474}\u{1F3FF}", 'CHARACTER_OLD_WOMAN' => "\u{1F475}", 'CHARACTER_OLD_WOMAN_LIGHT_SKIN_TONE' => "\u{1F475}\u{1F3FB}", 'CHARACTER_OLD_WOMAN_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F475}\u{1F3FC}", 'CHARACTER_OLD_WOMAN_MEDIUM_SKIN_TONE' => "\u{1F475}\u{1F3FD}", 'CHARACTER_OLD_WOMAN_MEDIUM_DARK_SKIN_TONE' => "\u{1F475}\u{1F3FE}", 'CHARACTER_OLD_WOMAN_DARK_SKIN_TONE' => "\u{1F475}\u{1F3FF}", 'CHARACTER_MAN_HEALTH_WORKER' => "\u{1F468}\u{200D}\u{2695}\u{FE0F}", 'CHARACTER_MAN_HEALTH_WORKER_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{2695}\u{FE0F}", 'CHARACTER_MAN_HEALTH_WORKER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{2695}\u{FE0F}", 'CHARACTER_MAN_HEALTH_WORKER_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{2695}\u{FE0F}", 'CHARACTER_MAN_HEALTH_WORKER_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{2695}\u{FE0F}", 'CHARACTER_MAN_HEALTH_WORKER_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{2695}\u{FE0F}", 'CHARACTER_WOMAN_HEALTH_WORKER' => "\u{1F469}\u{200D}\u{2695}\u{FE0F}", 'CHARACTER_WOMAN_HEALTH_WORKER_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{2695}\u{FE0F}", 'CHARACTER_WOMAN_HEALTH_WORKER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{2695}\u{FE0F}", 'CHARACTER_WOMAN_HEALTH_WORKER_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{2695}\u{FE0F}", 'CHARACTER_WOMAN_HEALTH_WORKER_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{2695}\u{FE0F}", 'CHARACTER_WOMAN_HEALTH_WORKER_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{2695}\u{FE0F}", 'CHARACTER_MAN_STUDENT' => "\u{1F468}\u{200D}\u{1F393}", 'CHARACTER_MAN_STUDENT_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{1F393}", 'CHARACTER_MAN_STUDENT_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{1F393}", 'CHARACTER_MAN_STUDENT_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{1F393}", 'CHARACTER_MAN_STUDENT_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{1F393}", 'CHARACTER_MAN_STUDENT_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{1F393}", 'CHARACTER_WOMAN_STUDENT' => "\u{1F469}\u{200D}\u{1F393}", 'CHARACTER_WOMAN_STUDENT_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{1F393}", 'CHARACTER_WOMAN_STUDENT_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{1F393}", 'CHARACTER_WOMAN_STUDENT_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{1F393}", 'CHARACTER_WOMAN_STUDENT_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{1F393}", 'CHARACTER_WOMAN_STUDENT_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{1F393}", 'CHARACTER_MAN_TEACHER' => "\u{1F468}\u{200D}\u{1F3EB}", 'CHARACTER_MAN_TEACHER_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{1F3EB}", 'CHARACTER_MAN_TEACHER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{1F3EB}", 'CHARACTER_MAN_TEACHER_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{1F3EB}", 'CHARACTER_MAN_TEACHER_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{1F3EB}", 'CHARACTER_MAN_TEACHER_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{1F3EB}", 'CHARACTER_WOMAN_TEACHER' => "\u{1F469}\u{200D}\u{1F3EB}", 'CHARACTER_WOMAN_TEACHER_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{1F3EB}", 'CHARACTER_WOMAN_TEACHER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{1F3EB}", 'CHARACTER_WOMAN_TEACHER_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{1F3EB}", 'CHARACTER_WOMAN_TEACHER_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{1F3EB}", 'CHARACTER_WOMAN_TEACHER_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{1F3EB}", 'CHARACTER_MAN_JUDGE' => "\u{1F468}\u{200D}\u{2696}\u{FE0F}", 'CHARACTER_MAN_JUDGE_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{2696}\u{FE0F}", 'CHARACTER_MAN_JUDGE_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{2696}\u{FE0F}", 'CHARACTER_MAN_JUDGE_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{2696}\u{FE0F}", 'CHARACTER_MAN_JUDGE_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{2696}\u{FE0F}", 'CHARACTER_MAN_JUDGE_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{2696}\u{FE0F}", 'CHARACTER_WOMAN_JUDGE' => "\u{1F469}\u{200D}\u{2696}\u{FE0F}", 'CHARACTER_WOMAN_JUDGE_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{2696}\u{FE0F}", 'CHARACTER_WOMAN_JUDGE_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{2696}\u{FE0F}", 'CHARACTER_WOMAN_JUDGE_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{2696}\u{FE0F}", 'CHARACTER_WOMAN_JUDGE_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{2696}\u{FE0F}", 'CHARACTER_WOMAN_JUDGE_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{2696}\u{FE0F}", 'CHARACTER_MAN_FARMER' => "\u{1F468}\u{200D}\u{1F33E}", 'CHARACTER_MAN_FARMER_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{1F33E}", 'CHARACTER_MAN_FARMER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{1F33E}", 'CHARACTER_MAN_FARMER_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{1F33E}", 'CHARACTER_MAN_FARMER_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{1F33E}", 'CHARACTER_MAN_FARMER_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{1F33E}", 'CHARACTER_WOMAN_FARMER' => "\u{1F469}\u{200D}\u{1F33E}", 'CHARACTER_WOMAN_FARMER_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{1F33E}", 'CHARACTER_WOMAN_FARMER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{1F33E}", 'CHARACTER_WOMAN_FARMER_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{1F33E}", 'CHARACTER_WOMAN_FARMER_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{1F33E}", 'CHARACTER_WOMAN_FARMER_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{1F33E}", 'CHARACTER_MAN_COOK' => "\u{1F468}\u{200D}\u{1F373}", 'CHARACTER_MAN_COOK_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{1F373}", 'CHARACTER_MAN_COOK_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{1F373}", 'CHARACTER_MAN_COOK_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{1F373}", 'CHARACTER_MAN_COOK_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{1F373}", 'CHARACTER_MAN_COOK_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{1F373}", 'CHARACTER_WOMAN_COOK' => "\u{1F469}\u{200D}\u{1F373}", 'CHARACTER_WOMAN_COOK_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{1F373}", 'CHARACTER_WOMAN_COOK_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{1F373}", 'CHARACTER_WOMAN_COOK_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{1F373}", 'CHARACTER_WOMAN_COOK_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{1F373}", 'CHARACTER_WOMAN_COOK_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{1F373}", 'CHARACTER_MAN_MECHANIC' => "\u{1F468}\u{200D}\u{1F527}", 'CHARACTER_MAN_MECHANIC_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{1F527}", 'CHARACTER_MAN_MECHANIC_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{1F527}", 'CHARACTER_MAN_MECHANIC_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{1F527}", 'CHARACTER_MAN_MECHANIC_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{1F527}", 'CHARACTER_MAN_MECHANIC_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{1F527}", 'CHARACTER_WOMAN_MECHANIC' => "\u{1F469}\u{200D}\u{1F527}", 'CHARACTER_WOMAN_MECHANIC_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{1F527}", 'CHARACTER_WOMAN_MECHANIC_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{1F527}", 'CHARACTER_WOMAN_MECHANIC_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{1F527}", 'CHARACTER_WOMAN_MECHANIC_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{1F527}", 'CHARACTER_WOMAN_MECHANIC_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{1F527}", 'CHARACTER_MAN_FACTORY_WORKER' => "\u{1F468}\u{200D}\u{1F3ED}", 'CHARACTER_MAN_FACTORY_WORKER_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{1F3ED}", 'CHARACTER_MAN_FACTORY_WORKER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{1F3ED}", 'CHARACTER_MAN_FACTORY_WORKER_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{1F3ED}", 'CHARACTER_MAN_FACTORY_WORKER_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{1F3ED}", 'CHARACTER_MAN_FACTORY_WORKER_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{1F3ED}", 'CHARACTER_WOMAN_FACTORY_WORKER' => "\u{1F469}\u{200D}\u{1F3ED}", 'CHARACTER_WOMAN_FACTORY_WORKER_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{1F3ED}", 'CHARACTER_WOMAN_FACTORY_WORKER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{1F3ED}", 'CHARACTER_WOMAN_FACTORY_WORKER_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{1F3ED}", 'CHARACTER_WOMAN_FACTORY_WORKER_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{1F3ED}", 'CHARACTER_WOMAN_FACTORY_WORKER_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{1F3ED}", 'CHARACTER_MAN_OFFICE_WORKER' => "\u{1F468}\u{200D}\u{1F4BC}", 'CHARACTER_MAN_OFFICE_WORKER_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{1F4BC}", 'CHARACTER_MAN_OFFICE_WORKER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{1F4BC}", 'CHARACTER_MAN_OFFICE_WORKER_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{1F4BC}", 'CHARACTER_MAN_OFFICE_WORKER_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{1F4BC}", 'CHARACTER_MAN_OFFICE_WORKER_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{1F4BC}", 'CHARACTER_WOMAN_OFFICE_WORKER' => "\u{1F469}\u{200D}\u{1F4BC}", 'CHARACTER_WOMAN_OFFICE_WORKER_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{1F4BC}", 'CHARACTER_WOMAN_OFFICE_WORKER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{1F4BC}", 'CHARACTER_WOMAN_OFFICE_WORKER_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{1F4BC}", 'CHARACTER_WOMAN_OFFICE_WORKER_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{1F4BC}", 'CHARACTER_WOMAN_OFFICE_WORKER_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{1F4BC}", 'CHARACTER_MAN_SCIENTIST' => "\u{1F468}\u{200D}\u{1F52C}", 'CHARACTER_MAN_SCIENTIST_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{1F52C}", 'CHARACTER_MAN_SCIENTIST_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{1F52C}", 'CHARACTER_MAN_SCIENTIST_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{1F52C}", 'CHARACTER_MAN_SCIENTIST_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{1F52C}", 'CHARACTER_MAN_SCIENTIST_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{1F52C}", 'CHARACTER_WOMAN_SCIENTIST' => "\u{1F469}\u{200D}\u{1F52C}", 'CHARACTER_WOMAN_SCIENTIST_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{1F52C}", 'CHARACTER_WOMAN_SCIENTIST_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{1F52C}", 'CHARACTER_WOMAN_SCIENTIST_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{1F52C}", 'CHARACTER_WOMAN_SCIENTIST_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{1F52C}", 'CHARACTER_WOMAN_SCIENTIST_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{1F52C}", 'CHARACTER_MAN_TECHNOLOGIST' => "\u{1F468}\u{200D}\u{1F4BB}", 'CHARACTER_MAN_TECHNOLOGIST_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{1F4BB}", 'CHARACTER_MAN_TECHNOLOGIST_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{1F4BB}", 'CHARACTER_MAN_TECHNOLOGIST_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{1F4BB}", 'CHARACTER_MAN_TECHNOLOGIST_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{1F4BB}", 'CHARACTER_MAN_TECHNOLOGIST_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{1F4BB}", 'CHARACTER_WOMAN_TECHNOLOGIST' => "\u{1F469}\u{200D}\u{1F4BB}", 'CHARACTER_WOMAN_TECHNOLOGIST_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{1F4BB}", 'CHARACTER_WOMAN_TECHNOLOGIST_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{1F4BB}", 'CHARACTER_WOMAN_TECHNOLOGIST_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{1F4BB}", 'CHARACTER_WOMAN_TECHNOLOGIST_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{1F4BB}", 'CHARACTER_WOMAN_TECHNOLOGIST_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{1F4BB}", 'CHARACTER_MAN_SINGER' => "\u{1F468}\u{200D}\u{1F3A4}", 'CHARACTER_MAN_SINGER_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{1F3A4}", 'CHARACTER_MAN_SINGER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{1F3A4}", 'CHARACTER_MAN_SINGER_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{1F3A4}", 'CHARACTER_MAN_SINGER_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{1F3A4}", 'CHARACTER_MAN_SINGER_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{1F3A4}", 'CHARACTER_WOMAN_SINGER' => "\u{1F469}\u{200D}\u{1F3A4}", 'CHARACTER_WOMAN_SINGER_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{1F3A4}", 'CHARACTER_WOMAN_SINGER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{1F3A4}", 'CHARACTER_WOMAN_SINGER_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{1F3A4}", 'CHARACTER_WOMAN_SINGER_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{1F3A4}", 'CHARACTER_WOMAN_SINGER_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{1F3A4}", 'CHARACTER_MAN_ARTIST' => "\u{1F468}\u{200D}\u{1F3A8}", 'CHARACTER_MAN_ARTIST_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{1F3A8}", 'CHARACTER_MAN_ARTIST_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{1F3A8}", 'CHARACTER_MAN_ARTIST_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{1F3A8}", 'CHARACTER_MAN_ARTIST_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{1F3A8}", 'CHARACTER_MAN_ARTIST_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{1F3A8}", 'CHARACTER_WOMAN_ARTIST' => "\u{1F469}\u{200D}\u{1F3A8}", 'CHARACTER_WOMAN_ARTIST_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{1F3A8}", 'CHARACTER_WOMAN_ARTIST_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{1F3A8}", 'CHARACTER_WOMAN_ARTIST_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{1F3A8}", 'CHARACTER_WOMAN_ARTIST_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{1F3A8}", 'CHARACTER_WOMAN_ARTIST_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{1F3A8}", 'CHARACTER_MAN_PILOT' => "\u{1F468}\u{200D}\u{2708}\u{FE0F}", 'CHARACTER_MAN_PILOT_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{2708}\u{FE0F}", 'CHARACTER_MAN_PILOT_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{2708}\u{FE0F}", 'CHARACTER_MAN_PILOT_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{2708}\u{FE0F}", 'CHARACTER_MAN_PILOT_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{2708}\u{FE0F}", 'CHARACTER_MAN_PILOT_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{2708}\u{FE0F}", 'CHARACTER_WOMAN_PILOT' => "\u{1F469}\u{200D}\u{2708}\u{FE0F}", 'CHARACTER_WOMAN_PILOT_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{2708}\u{FE0F}", 'CHARACTER_WOMAN_PILOT_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{2708}\u{FE0F}", 'CHARACTER_WOMAN_PILOT_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{2708}\u{FE0F}", 'CHARACTER_WOMAN_PILOT_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{2708}\u{FE0F}", 'CHARACTER_WOMAN_PILOT_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{2708}\u{FE0F}", 'CHARACTER_MAN_ASTRONAUT' => "\u{1F468}\u{200D}\u{1F680}", 'CHARACTER_MAN_ASTRONAUT_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{1F680}", 'CHARACTER_MAN_ASTRONAUT_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{1F680}", 'CHARACTER_MAN_ASTRONAUT_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{1F680}", 'CHARACTER_MAN_ASTRONAUT_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{1F680}", 'CHARACTER_MAN_ASTRONAUT_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{1F680}", 'CHARACTER_WOMAN_ASTRONAUT' => "\u{1F469}\u{200D}\u{1F680}", 'CHARACTER_WOMAN_ASTRONAUT_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{1F680}", 'CHARACTER_WOMAN_ASTRONAUT_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{1F680}", 'CHARACTER_WOMAN_ASTRONAUT_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{1F680}", 'CHARACTER_WOMAN_ASTRONAUT_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{1F680}", 'CHARACTER_WOMAN_ASTRONAUT_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{1F680}", 'CHARACTER_MAN_FIREFIGHTER' => "\u{1F468}\u{200D}\u{1F692}", 'CHARACTER_MAN_FIREFIGHTER_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{1F692}", 'CHARACTER_MAN_FIREFIGHTER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{1F692}", 'CHARACTER_MAN_FIREFIGHTER_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{1F692}", 'CHARACTER_MAN_FIREFIGHTER_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{1F692}", 'CHARACTER_MAN_FIREFIGHTER_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{1F692}", 'CHARACTER_WOMAN_FIREFIGHTER' => "\u{1F469}\u{200D}\u{1F692}", 'CHARACTER_WOMAN_FIREFIGHTER_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{1F692}", 'CHARACTER_WOMAN_FIREFIGHTER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{1F692}", 'CHARACTER_WOMAN_FIREFIGHTER_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{1F692}", 'CHARACTER_WOMAN_FIREFIGHTER_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{1F692}", 'CHARACTER_WOMAN_FIREFIGHTER_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{1F692}", 'CHARACTER_POLICE_OFFICER' => "\u{1F46E}", 'CHARACTER_POLICE_OFFICER_LIGHT_SKIN_TONE' => "\u{1F46E}\u{1F3FB}", 'CHARACTER_POLICE_OFFICER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F46E}\u{1F3FC}", 'CHARACTER_POLICE_OFFICER_MEDIUM_SKIN_TONE' => "\u{1F46E}\u{1F3FD}", 'CHARACTER_POLICE_OFFICER_MEDIUM_DARK_SKIN_TONE' => "\u{1F46E}\u{1F3FE}", 'CHARACTER_POLICE_OFFICER_DARK_SKIN_TONE' => "\u{1F46E}\u{1F3FF}", 'CHARACTER_MAN_POLICE_OFFICER' => "\u{1F46E}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_POLICE_OFFICER_LIGHT_SKIN_TONE' => "\u{1F46E}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_POLICE_OFFICER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F46E}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_POLICE_OFFICER_MEDIUM_SKIN_TONE' => "\u{1F46E}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_POLICE_OFFICER_MEDIUM_DARK_SKIN_TONE' => "\u{1F46E}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_POLICE_OFFICER_DARK_SKIN_TONE' => "\u{1F46E}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_POLICE_OFFICER' => "\u{1F46E}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_POLICE_OFFICER_LIGHT_SKIN_TONE' => "\u{1F46E}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_POLICE_OFFICER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F46E}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_POLICE_OFFICER_MEDIUM_SKIN_TONE' => "\u{1F46E}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_POLICE_OFFICER_MEDIUM_DARK_SKIN_TONE' => "\u{1F46E}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_POLICE_OFFICER_DARK_SKIN_TONE' => "\u{1F46E}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_DETECTIVE' => "\u{1F575}\u{FE0F}", 'CHARACTER_DETECTIVE_LIGHT_SKIN_TONE' => "\u{1F575}\u{1F3FB}", 'CHARACTER_DETECTIVE_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F575}\u{1F3FC}", 'CHARACTER_DETECTIVE_MEDIUM_SKIN_TONE' => "\u{1F575}\u{1F3FD}", 'CHARACTER_DETECTIVE_MEDIUM_DARK_SKIN_TONE' => "\u{1F575}\u{1F3FE}", 'CHARACTER_DETECTIVE_DARK_SKIN_TONE' => "\u{1F575}\u{1F3FF}", 'CHARACTER_MAN_DETECTIVE' => "\u{1F575}\u{FE0F}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_DETECTIVE_LIGHT_SKIN_TONE' => "\u{1F575}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_DETECTIVE_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F575}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_DETECTIVE_MEDIUM_SKIN_TONE' => "\u{1F575}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_DETECTIVE_MEDIUM_DARK_SKIN_TONE' => "\u{1F575}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_DETECTIVE_DARK_SKIN_TONE' => "\u{1F575}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_DETECTIVE' => "\u{1F575}\u{FE0F}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_DETECTIVE_LIGHT_SKIN_TONE' => "\u{1F575}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_DETECTIVE_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F575}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_DETECTIVE_MEDIUM_SKIN_TONE' => "\u{1F575}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_DETECTIVE_MEDIUM_DARK_SKIN_TONE' => "\u{1F575}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_DETECTIVE_DARK_SKIN_TONE' => "\u{1F575}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_GUARD' => "\u{1F482}", 'CHARACTER_GUARD_LIGHT_SKIN_TONE' => "\u{1F482}\u{1F3FB}", 'CHARACTER_GUARD_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F482}\u{1F3FC}", 'CHARACTER_GUARD_MEDIUM_SKIN_TONE' => "\u{1F482}\u{1F3FD}", 'CHARACTER_GUARD_MEDIUM_DARK_SKIN_TONE' => "\u{1F482}\u{1F3FE}", 'CHARACTER_GUARD_DARK_SKIN_TONE' => "\u{1F482}\u{1F3FF}", 'CHARACTER_MAN_GUARD' => "\u{1F482}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GUARD_LIGHT_SKIN_TONE' => "\u{1F482}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GUARD_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F482}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GUARD_MEDIUM_SKIN_TONE' => "\u{1F482}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GUARD_MEDIUM_DARK_SKIN_TONE' => "\u{1F482}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GUARD_DARK_SKIN_TONE' => "\u{1F482}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_GUARD' => "\u{1F482}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GUARD_LIGHT_SKIN_TONE' => "\u{1F482}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GUARD_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F482}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GUARD_MEDIUM_SKIN_TONE' => "\u{1F482}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GUARD_MEDIUM_DARK_SKIN_TONE' => "\u{1F482}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GUARD_DARK_SKIN_TONE' => "\u{1F482}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_CONSTRUCTION_WORKER' => "\u{1F477}", 'CHARACTER_CONSTRUCTION_WORKER_LIGHT_SKIN_TONE' => "\u{1F477}\u{1F3FB}", 'CHARACTER_CONSTRUCTION_WORKER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F477}\u{1F3FC}", 'CHARACTER_CONSTRUCTION_WORKER_MEDIUM_SKIN_TONE' => "\u{1F477}\u{1F3FD}", 'CHARACTER_CONSTRUCTION_WORKER_MEDIUM_DARK_SKIN_TONE' => "\u{1F477}\u{1F3FE}", 'CHARACTER_CONSTRUCTION_WORKER_DARK_SKIN_TONE' => "\u{1F477}\u{1F3FF}", 'CHARACTER_MAN_CONSTRUCTION_WORKER' => "\u{1F477}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_CONSTRUCTION_WORKER_LIGHT_SKIN_TONE' => "\u{1F477}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_CONSTRUCTION_WORKER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F477}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_CONSTRUCTION_WORKER_MEDIUM_SKIN_TONE' => "\u{1F477}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_CONSTRUCTION_WORKER_MEDIUM_DARK_SKIN_TONE' => "\u{1F477}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_CONSTRUCTION_WORKER_DARK_SKIN_TONE' => "\u{1F477}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_CONSTRUCTION_WORKER' => "\u{1F477}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_CONSTRUCTION_WORKER_LIGHT_SKIN_TONE' => "\u{1F477}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_CONSTRUCTION_WORKER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F477}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_CONSTRUCTION_WORKER_MEDIUM_SKIN_TONE' => "\u{1F477}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_CONSTRUCTION_WORKER_MEDIUM_DARK_SKIN_TONE' => "\u{1F477}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_CONSTRUCTION_WORKER_DARK_SKIN_TONE' => "\u{1F477}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PRINCE' => "\u{1F934}", 'CHARACTER_PRINCE_LIGHT_SKIN_TONE' => "\u{1F934}\u{1F3FB}", 'CHARACTER_PRINCE_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F934}\u{1F3FC}", 'CHARACTER_PRINCE_MEDIUM_SKIN_TONE' => "\u{1F934}\u{1F3FD}", 'CHARACTER_PRINCE_MEDIUM_DARK_SKIN_TONE' => "\u{1F934}\u{1F3FE}", 'CHARACTER_PRINCE_DARK_SKIN_TONE' => "\u{1F934}\u{1F3FF}", 'CHARACTER_PRINCESS' => "\u{1F478}", 'CHARACTER_PRINCESS_LIGHT_SKIN_TONE' => "\u{1F478}\u{1F3FB}", 'CHARACTER_PRINCESS_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F478}\u{1F3FC}", 'CHARACTER_PRINCESS_MEDIUM_SKIN_TONE' => "\u{1F478}\u{1F3FD}", 'CHARACTER_PRINCESS_MEDIUM_DARK_SKIN_TONE' => "\u{1F478}\u{1F3FE}", 'CHARACTER_PRINCESS_DARK_SKIN_TONE' => "\u{1F478}\u{1F3FF}", 'CHARACTER_PERSON_WEARING_TURBAN' => "\u{1F473}", 'CHARACTER_PERSON_WEARING_TURBAN_LIGHT_SKIN_TONE' => "\u{1F473}\u{1F3FB}", 'CHARACTER_PERSON_WEARING_TURBAN_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F473}\u{1F3FC}", 'CHARACTER_PERSON_WEARING_TURBAN_MEDIUM_SKIN_TONE' => "\u{1F473}\u{1F3FD}", 'CHARACTER_PERSON_WEARING_TURBAN_MEDIUM_DARK_SKIN_TONE' => "\u{1F473}\u{1F3FE}", 'CHARACTER_PERSON_WEARING_TURBAN_DARK_SKIN_TONE' => "\u{1F473}\u{1F3FF}", 'CHARACTER_MAN_WEARING_TURBAN' => "\u{1F473}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_WEARING_TURBAN_LIGHT_SKIN_TONE' => "\u{1F473}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_WEARING_TURBAN_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F473}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_WEARING_TURBAN_MEDIUM_SKIN_TONE' => "\u{1F473}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_WEARING_TURBAN_MEDIUM_DARK_SKIN_TONE' => "\u{1F473}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_WEARING_TURBAN_DARK_SKIN_TONE' => "\u{1F473}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_WEARING_TURBAN' => "\u{1F473}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_WEARING_TURBAN_LIGHT_SKIN_TONE' => "\u{1F473}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_WEARING_TURBAN_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F473}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_WEARING_TURBAN_MEDIUM_SKIN_TONE' => "\u{1F473}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_WEARING_TURBAN_MEDIUM_DARK_SKIN_TONE' => "\u{1F473}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_WEARING_TURBAN_DARK_SKIN_TONE' => "\u{1F473}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MAN_WITH_CHINESE_CAP' => "\u{1F472}", 'CHARACTER_MAN_WITH_CHINESE_CAP_LIGHT_SKIN_TONE' => "\u{1F472}\u{1F3FB}", 'CHARACTER_MAN_WITH_CHINESE_CAP_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F472}\u{1F3FC}", 'CHARACTER_MAN_WITH_CHINESE_CAP_MEDIUM_SKIN_TONE' => "\u{1F472}\u{1F3FD}", 'CHARACTER_MAN_WITH_CHINESE_CAP_MEDIUM_DARK_SKIN_TONE' => "\u{1F472}\u{1F3FE}", 'CHARACTER_MAN_WITH_CHINESE_CAP_DARK_SKIN_TONE' => "\u{1F472}\u{1F3FF}", 'CHARACTER_WOMAN_WITH_HEADSCARF' => "\u{1F9D5}", 'CHARACTER_WOMAN_WITH_HEADSCARF_LIGHT_SKIN_TONE' => "\u{1F9D5}\u{1F3FB}", 'CHARACTER_WOMAN_WITH_HEADSCARF_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9D5}\u{1F3FC}", 'CHARACTER_WOMAN_WITH_HEADSCARF_MEDIUM_SKIN_TONE' => "\u{1F9D5}\u{1F3FD}", 'CHARACTER_WOMAN_WITH_HEADSCARF_MEDIUM_DARK_SKIN_TONE' => "\u{1F9D5}\u{1F3FE}", 'CHARACTER_WOMAN_WITH_HEADSCARF_DARK_SKIN_TONE' => "\u{1F9D5}\u{1F3FF}", 'CHARACTER_BEARDED_PERSON' => "\u{1F9D4}", 'CHARACTER_BEARDED_PERSON_LIGHT_SKIN_TONE' => "\u{1F9D4}\u{1F3FB}", 'CHARACTER_BEARDED_PERSON_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9D4}\u{1F3FC}", 'CHARACTER_BEARDED_PERSON_MEDIUM_SKIN_TONE' => "\u{1F9D4}\u{1F3FD}", 'CHARACTER_BEARDED_PERSON_MEDIUM_DARK_SKIN_TONE' => "\u{1F9D4}\u{1F3FE}", 'CHARACTER_BEARDED_PERSON_DARK_SKIN_TONE' => "\u{1F9D4}\u{1F3FF}", 'CHARACTER_BLOND_HAIRED_PERSON' => "\u{1F471}", 'CHARACTER_BLOND_HAIRED_PERSON_LIGHT_SKIN_TONE' => "\u{1F471}\u{1F3FB}", 'CHARACTER_BLOND_HAIRED_PERSON_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F471}\u{1F3FC}", 'CHARACTER_BLOND_HAIRED_PERSON_MEDIUM_SKIN_TONE' => "\u{1F471}\u{1F3FD}", 'CHARACTER_BLOND_HAIRED_PERSON_MEDIUM_DARK_SKIN_TONE' => "\u{1F471}\u{1F3FE}", 'CHARACTER_BLOND_HAIRED_PERSON_DARK_SKIN_TONE' => "\u{1F471}\u{1F3FF}", 'CHARACTER_BLOND_HAIRED_MAN' => "\u{1F471}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_BLOND_HAIRED_MAN_LIGHT_SKIN_TONE' => "\u{1F471}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_BLOND_HAIRED_MAN_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F471}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_BLOND_HAIRED_MAN_MEDIUM_SKIN_TONE' => "\u{1F471}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_BLOND_HAIRED_MAN_MEDIUM_DARK_SKIN_TONE' => "\u{1F471}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_BLOND_HAIRED_MAN_DARK_SKIN_TONE' => "\u{1F471}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_BLOND_HAIRED_WOMAN' => "\u{1F471}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_BLOND_HAIRED_WOMAN_LIGHT_SKIN_TONE' => "\u{1F471}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_BLOND_HAIRED_WOMAN_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F471}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_BLOND_HAIRED_WOMAN_MEDIUM_SKIN_TONE' => "\u{1F471}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_BLOND_HAIRED_WOMAN_MEDIUM_DARK_SKIN_TONE' => "\u{1F471}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_BLOND_HAIRED_WOMAN_DARK_SKIN_TONE' => "\u{1F471}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MAN_RED_HAIRED' => "\u{1F468}\u{200D}\u{1F9B0}", 'CHARACTER_MAN_RED_HAIRED_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{1F9B0}", 'CHARACTER_MAN_RED_HAIRED_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{1F9B0}", 'CHARACTER_MAN_RED_HAIRED_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{1F9B0}", 'CHARACTER_MAN_RED_HAIRED_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{1F9B0}", 'CHARACTER_MAN_RED_HAIRED_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{1F9B0}", 'CHARACTER_WOMAN_RED_HAIRED' => "\u{1F469}\u{200D}\u{1F9B0}", 'CHARACTER_WOMAN_RED_HAIRED_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{1F9B0}", 'CHARACTER_WOMAN_RED_HAIRED_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{1F9B0}", 'CHARACTER_WOMAN_RED_HAIRED_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{1F9B0}", 'CHARACTER_WOMAN_RED_HAIRED_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{1F9B0}", 'CHARACTER_WOMAN_RED_HAIRED_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{1F9B0}", 'CHARACTER_MAN_CURLY_HAIRED' => "\u{1F468}\u{200D}\u{1F9B1}", 'CHARACTER_MAN_CURLY_HAIRED_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{1F9B1}", 'CHARACTER_MAN_CURLY_HAIRED_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{1F9B1}", 'CHARACTER_MAN_CURLY_HAIRED_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{1F9B1}", 'CHARACTER_MAN_CURLY_HAIRED_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{1F9B1}", 'CHARACTER_MAN_CURLY_HAIRED_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{1F9B1}", 'CHARACTER_WOMAN_CURLY_HAIRED' => "\u{1F469}\u{200D}\u{1F9B1}", 'CHARACTER_WOMAN_CURLY_HAIRED_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{1F9B1}", 'CHARACTER_WOMAN_CURLY_HAIRED_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{1F9B1}", 'CHARACTER_WOMAN_CURLY_HAIRED_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{1F9B1}", 'CHARACTER_WOMAN_CURLY_HAIRED_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{1F9B1}", 'CHARACTER_WOMAN_CURLY_HAIRED_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{1F9B1}", 'CHARACTER_MAN_BALD' => "\u{1F468}\u{200D}\u{1F9B2}", 'CHARACTER_MAN_BALD_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{1F9B2}", 'CHARACTER_MAN_BALD_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{1F9B2}", 'CHARACTER_MAN_BALD_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{1F9B2}", 'CHARACTER_MAN_BALD_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{1F9B2}", 'CHARACTER_MAN_BALD_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{1F9B2}", 'CHARACTER_WOMAN_BALD' => "\u{1F469}\u{200D}\u{1F9B2}", 'CHARACTER_WOMAN_BALD_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{1F9B2}", 'CHARACTER_WOMAN_BALD_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{1F9B2}", 'CHARACTER_WOMAN_BALD_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{1F9B2}", 'CHARACTER_WOMAN_BALD_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{1F9B2}", 'CHARACTER_WOMAN_BALD_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{1F9B2}", 'CHARACTER_MAN_WHITE_HAIRED' => "\u{1F468}\u{200D}\u{1F9B3}", 'CHARACTER_MAN_WHITE_HAIRED_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FB}\u{200D}\u{1F9B3}", 'CHARACTER_MAN_WHITE_HAIRED_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F468}\u{1F3FC}\u{200D}\u{1F9B3}", 'CHARACTER_MAN_WHITE_HAIRED_MEDIUM_SKIN_TONE' => "\u{1F468}\u{1F3FD}\u{200D}\u{1F9B3}", 'CHARACTER_MAN_WHITE_HAIRED_MEDIUM_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FE}\u{200D}\u{1F9B3}", 'CHARACTER_MAN_WHITE_HAIRED_DARK_SKIN_TONE' => "\u{1F468}\u{1F3FF}\u{200D}\u{1F9B3}", 'CHARACTER_WOMAN_WHITE_HAIRED' => "\u{1F469}\u{200D}\u{1F9B3}", 'CHARACTER_WOMAN_WHITE_HAIRED_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FB}\u{200D}\u{1F9B3}", 'CHARACTER_WOMAN_WHITE_HAIRED_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F469}\u{1F3FC}\u{200D}\u{1F9B3}", 'CHARACTER_WOMAN_WHITE_HAIRED_MEDIUM_SKIN_TONE' => "\u{1F469}\u{1F3FD}\u{200D}\u{1F9B3}", 'CHARACTER_WOMAN_WHITE_HAIRED_MEDIUM_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FE}\u{200D}\u{1F9B3}", 'CHARACTER_WOMAN_WHITE_HAIRED_DARK_SKIN_TONE' => "\u{1F469}\u{1F3FF}\u{200D}\u{1F9B3}", 'CHARACTER_MAN_IN_TUXEDO' => "\u{1F935}", 'CHARACTER_MAN_IN_TUXEDO_LIGHT_SKIN_TONE' => "\u{1F935}\u{1F3FB}", 'CHARACTER_MAN_IN_TUXEDO_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F935}\u{1F3FC}", 'CHARACTER_MAN_IN_TUXEDO_MEDIUM_SKIN_TONE' => "\u{1F935}\u{1F3FD}", 'CHARACTER_MAN_IN_TUXEDO_MEDIUM_DARK_SKIN_TONE' => "\u{1F935}\u{1F3FE}", 'CHARACTER_MAN_IN_TUXEDO_DARK_SKIN_TONE' => "\u{1F935}\u{1F3FF}", 'CHARACTER_BRIDE_WITH_VEIL' => "\u{1F470}", 'CHARACTER_BRIDE_WITH_VEIL_LIGHT_SKIN_TONE' => "\u{1F470}\u{1F3FB}", 'CHARACTER_BRIDE_WITH_VEIL_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F470}\u{1F3FC}", 'CHARACTER_BRIDE_WITH_VEIL_MEDIUM_SKIN_TONE' => "\u{1F470}\u{1F3FD}", 'CHARACTER_BRIDE_WITH_VEIL_MEDIUM_DARK_SKIN_TONE' => "\u{1F470}\u{1F3FE}", 'CHARACTER_BRIDE_WITH_VEIL_DARK_SKIN_TONE' => "\u{1F470}\u{1F3FF}", 'CHARACTER_PREGNANT_WOMAN' => "\u{1F930}", 'CHARACTER_PREGNANT_WOMAN_LIGHT_SKIN_TONE' => "\u{1F930}\u{1F3FB}", 'CHARACTER_PREGNANT_WOMAN_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F930}\u{1F3FC}", 'CHARACTER_PREGNANT_WOMAN_MEDIUM_SKIN_TONE' => "\u{1F930}\u{1F3FD}", 'CHARACTER_PREGNANT_WOMAN_MEDIUM_DARK_SKIN_TONE' => "\u{1F930}\u{1F3FE}", 'CHARACTER_PREGNANT_WOMAN_DARK_SKIN_TONE' => "\u{1F930}\u{1F3FF}", 'CHARACTER_BREAST_FEEDING' => "\u{1F931}", 'CHARACTER_BREAST_FEEDING_LIGHT_SKIN_TONE' => "\u{1F931}\u{1F3FB}", 'CHARACTER_BREAST_FEEDING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F931}\u{1F3FC}", 'CHARACTER_BREAST_FEEDING_MEDIUM_SKIN_TONE' => "\u{1F931}\u{1F3FD}", 'CHARACTER_BREAST_FEEDING_MEDIUM_DARK_SKIN_TONE' => "\u{1F931}\u{1F3FE}", 'CHARACTER_BREAST_FEEDING_DARK_SKIN_TONE' => "\u{1F931}\u{1F3FF}", 'CHARACTER_BABY_ANGEL' => "\u{1F47C}", 'CHARACTER_BABY_ANGEL_LIGHT_SKIN_TONE' => "\u{1F47C}\u{1F3FB}", 'CHARACTER_BABY_ANGEL_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F47C}\u{1F3FC}", 'CHARACTER_BABY_ANGEL_MEDIUM_SKIN_TONE' => "\u{1F47C}\u{1F3FD}", 'CHARACTER_BABY_ANGEL_MEDIUM_DARK_SKIN_TONE' => "\u{1F47C}\u{1F3FE}", 'CHARACTER_BABY_ANGEL_DARK_SKIN_TONE' => "\u{1F47C}\u{1F3FF}", 'CHARACTER_SANTA_CLAUS' => "\u{1F385}", 'CHARACTER_SANTA_CLAUS_LIGHT_SKIN_TONE' => "\u{1F385}\u{1F3FB}", 'CHARACTER_SANTA_CLAUS_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F385}\u{1F3FC}", 'CHARACTER_SANTA_CLAUS_MEDIUM_SKIN_TONE' => "\u{1F385}\u{1F3FD}", 'CHARACTER_SANTA_CLAUS_MEDIUM_DARK_SKIN_TONE' => "\u{1F385}\u{1F3FE}", 'CHARACTER_SANTA_CLAUS_DARK_SKIN_TONE' => "\u{1F385}\u{1F3FF}", 'CHARACTER_MRS_CLAUS' => "\u{1F936}", 'CHARACTER_MRS_CLAUS_LIGHT_SKIN_TONE' => "\u{1F936}\u{1F3FB}", 'CHARACTER_MRS_CLAUS_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F936}\u{1F3FC}", 'CHARACTER_MRS_CLAUS_MEDIUM_SKIN_TONE' => "\u{1F936}\u{1F3FD}", 'CHARACTER_MRS_CLAUS_MEDIUM_DARK_SKIN_TONE' => "\u{1F936}\u{1F3FE}", 'CHARACTER_MRS_CLAUS_DARK_SKIN_TONE' => "\u{1F936}\u{1F3FF}", 'CHARACTER_SUPERHERO' => "\u{1F9B8}", 'CHARACTER_SUPERHERO_LIGHT_SKIN_TONE' => "\u{1F9B8}\u{1F3FB}", 'CHARACTER_SUPERHERO_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9B8}\u{1F3FC}", 'CHARACTER_SUPERHERO_MEDIUM_SKIN_TONE' => "\u{1F9B8}\u{1F3FD}", 'CHARACTER_SUPERHERO_MEDIUM_DARK_SKIN_TONE' => "\u{1F9B8}\u{1F3FE}", 'CHARACTER_SUPERHERO_DARK_SKIN_TONE' => "\u{1F9B8}\u{1F3FF}", 'CHARACTER_WOMAN_SUPERHERO' => "\u{1F9B8}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SUPERHERO_LIGHT_SKIN_TONE' => "\u{1F9B8}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SUPERHERO_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9B8}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SUPERHERO_MEDIUM_SKIN_TONE' => "\u{1F9B8}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SUPERHERO_MEDIUM_DARK_SKIN_TONE' => "\u{1F9B8}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SUPERHERO_DARK_SKIN_TONE' => "\u{1F9B8}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MAN_SUPERHERO' => "\u{1F9B8}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SUPERHERO_LIGHT_SKIN_TONE' => "\u{1F9B8}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SUPERHERO_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9B8}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SUPERHERO_MEDIUM_SKIN_TONE' => "\u{1F9B8}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SUPERHERO_MEDIUM_DARK_SKIN_TONE' => "\u{1F9B8}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SUPERHERO_DARK_SKIN_TONE' => "\u{1F9B8}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_SUPERVILLAIN' => "\u{1F9B9}", 'CHARACTER_SUPERVILLAIN_LIGHT_SKIN_TONE' => "\u{1F9B9}\u{1F3FB}", 'CHARACTER_SUPERVILLAIN_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9B9}\u{1F3FC}", 'CHARACTER_SUPERVILLAIN_MEDIUM_SKIN_TONE' => "\u{1F9B9}\u{1F3FD}", 'CHARACTER_SUPERVILLAIN_MEDIUM_DARK_SKIN_TONE' => "\u{1F9B9}\u{1F3FE}", 'CHARACTER_SUPERVILLAIN_DARK_SKIN_TONE' => "\u{1F9B9}\u{1F3FF}", 'CHARACTER_WOMAN_SUPERVILLAIN' => "\u{1F9B9}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SUPERVILLAIN_LIGHT_SKIN_TONE' => "\u{1F9B9}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SUPERVILLAIN_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9B9}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SUPERVILLAIN_MEDIUM_SKIN_TONE' => "\u{1F9B9}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SUPERVILLAIN_MEDIUM_DARK_SKIN_TONE' => "\u{1F9B9}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SUPERVILLAIN_DARK_SKIN_TONE' => "\u{1F9B9}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MAN_SUPERVILLAIN' => "\u{1F9B9}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SUPERVILLAIN_LIGHT_SKIN_TONE' => "\u{1F9B9}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SUPERVILLAIN_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9B9}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SUPERVILLAIN_MEDIUM_SKIN_TONE' => "\u{1F9B9}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SUPERVILLAIN_MEDIUM_DARK_SKIN_TONE' => "\u{1F9B9}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SUPERVILLAIN_DARK_SKIN_TONE' => "\u{1F9B9}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAGE' => "\u{1F9D9}", 'CHARACTER_MAGE_LIGHT_SKIN_TONE' => "\u{1F9D9}\u{1F3FB}", 'CHARACTER_MAGE_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9D9}\u{1F3FC}", 'CHARACTER_MAGE_MEDIUM_SKIN_TONE' => "\u{1F9D9}\u{1F3FD}", 'CHARACTER_MAGE_MEDIUM_DARK_SKIN_TONE' => "\u{1F9D9}\u{1F3FE}", 'CHARACTER_MAGE_DARK_SKIN_TONE' => "\u{1F9D9}\u{1F3FF}", 'CHARACTER_WOMAN_MAGE' => "\u{1F9D9}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_MAGE_LIGHT_SKIN_TONE' => "\u{1F9D9}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_MAGE_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9D9}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_MAGE_MEDIUM_SKIN_TONE' => "\u{1F9D9}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_MAGE_MEDIUM_DARK_SKIN_TONE' => "\u{1F9D9}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_MAGE_DARK_SKIN_TONE' => "\u{1F9D9}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MAN_MAGE' => "\u{1F9D9}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_MAGE_LIGHT_SKIN_TONE' => "\u{1F9D9}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_MAGE_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9D9}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_MAGE_MEDIUM_SKIN_TONE' => "\u{1F9D9}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_MAGE_MEDIUM_DARK_SKIN_TONE' => "\u{1F9D9}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_MAGE_DARK_SKIN_TONE' => "\u{1F9D9}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_FAIRY' => "\u{1F9DA}", 'CHARACTER_FAIRY_LIGHT_SKIN_TONE' => "\u{1F9DA}\u{1F3FB}", 'CHARACTER_FAIRY_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9DA}\u{1F3FC}", 'CHARACTER_FAIRY_MEDIUM_SKIN_TONE' => "\u{1F9DA}\u{1F3FD}", 'CHARACTER_FAIRY_MEDIUM_DARK_SKIN_TONE' => "\u{1F9DA}\u{1F3FE}", 'CHARACTER_FAIRY_DARK_SKIN_TONE' => "\u{1F9DA}\u{1F3FF}", 'CHARACTER_WOMAN_FAIRY' => "\u{1F9DA}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_FAIRY_LIGHT_SKIN_TONE' => "\u{1F9DA}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_FAIRY_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9DA}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_FAIRY_MEDIUM_SKIN_TONE' => "\u{1F9DA}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_FAIRY_MEDIUM_DARK_SKIN_TONE' => "\u{1F9DA}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_FAIRY_DARK_SKIN_TONE' => "\u{1F9DA}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MAN_FAIRY' => "\u{1F9DA}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_FAIRY_LIGHT_SKIN_TONE' => "\u{1F9DA}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_FAIRY_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9DA}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_FAIRY_MEDIUM_SKIN_TONE' => "\u{1F9DA}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_FAIRY_MEDIUM_DARK_SKIN_TONE' => "\u{1F9DA}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_FAIRY_DARK_SKIN_TONE' => "\u{1F9DA}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_VAMPIRE' => "\u{1F9DB}", 'CHARACTER_VAMPIRE_LIGHT_SKIN_TONE' => "\u{1F9DB}\u{1F3FB}", 'CHARACTER_VAMPIRE_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9DB}\u{1F3FC}", 'CHARACTER_VAMPIRE_MEDIUM_SKIN_TONE' => "\u{1F9DB}\u{1F3FD}", 'CHARACTER_VAMPIRE_MEDIUM_DARK_SKIN_TONE' => "\u{1F9DB}\u{1F3FE}", 'CHARACTER_VAMPIRE_DARK_SKIN_TONE' => "\u{1F9DB}\u{1F3FF}", 'CHARACTER_WOMAN_VAMPIRE' => "\u{1F9DB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_VAMPIRE_LIGHT_SKIN_TONE' => "\u{1F9DB}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_VAMPIRE_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9DB}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_VAMPIRE_MEDIUM_SKIN_TONE' => "\u{1F9DB}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_VAMPIRE_MEDIUM_DARK_SKIN_TONE' => "\u{1F9DB}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_VAMPIRE_DARK_SKIN_TONE' => "\u{1F9DB}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MAN_VAMPIRE' => "\u{1F9DB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_VAMPIRE_LIGHT_SKIN_TONE' => "\u{1F9DB}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_VAMPIRE_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9DB}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_VAMPIRE_MEDIUM_SKIN_TONE' => "\u{1F9DB}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_VAMPIRE_MEDIUM_DARK_SKIN_TONE' => "\u{1F9DB}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_VAMPIRE_DARK_SKIN_TONE' => "\u{1F9DB}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MERPERSON' => "\u{1F9DC}", 'CHARACTER_MERPERSON_LIGHT_SKIN_TONE' => "\u{1F9DC}\u{1F3FB}", 'CHARACTER_MERPERSON_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9DC}\u{1F3FC}", 'CHARACTER_MERPERSON_MEDIUM_SKIN_TONE' => "\u{1F9DC}\u{1F3FD}", 'CHARACTER_MERPERSON_MEDIUM_DARK_SKIN_TONE' => "\u{1F9DC}\u{1F3FE}", 'CHARACTER_MERPERSON_DARK_SKIN_TONE' => "\u{1F9DC}\u{1F3FF}", 'CHARACTER_MERMAID' => "\u{1F9DC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MERMAID_LIGHT_SKIN_TONE' => "\u{1F9DC}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MERMAID_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9DC}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MERMAID_MEDIUM_SKIN_TONE' => "\u{1F9DC}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MERMAID_MEDIUM_DARK_SKIN_TONE' => "\u{1F9DC}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MERMAID_DARK_SKIN_TONE' => "\u{1F9DC}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MERMAN' => "\u{1F9DC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MERMAN_LIGHT_SKIN_TONE' => "\u{1F9DC}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MERMAN_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9DC}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MERMAN_MEDIUM_SKIN_TONE' => "\u{1F9DC}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MERMAN_MEDIUM_DARK_SKIN_TONE' => "\u{1F9DC}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MERMAN_DARK_SKIN_TONE' => "\u{1F9DC}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_ELF' => "\u{1F9DD}", 'CHARACTER_ELF_LIGHT_SKIN_TONE' => "\u{1F9DD}\u{1F3FB}", 'CHARACTER_ELF_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9DD}\u{1F3FC}", 'CHARACTER_ELF_MEDIUM_SKIN_TONE' => "\u{1F9DD}\u{1F3FD}", 'CHARACTER_ELF_MEDIUM_DARK_SKIN_TONE' => "\u{1F9DD}\u{1F3FE}", 'CHARACTER_ELF_DARK_SKIN_TONE' => "\u{1F9DD}\u{1F3FF}", 'CHARACTER_WOMAN_ELF' => "\u{1F9DD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_ELF_LIGHT_SKIN_TONE' => "\u{1F9DD}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_ELF_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9DD}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_ELF_MEDIUM_SKIN_TONE' => "\u{1F9DD}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_ELF_MEDIUM_DARK_SKIN_TONE' => "\u{1F9DD}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_ELF_DARK_SKIN_TONE' => "\u{1F9DD}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MAN_ELF' => "\u{1F9DD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_ELF_LIGHT_SKIN_TONE' => "\u{1F9DD}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_ELF_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9DD}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_ELF_MEDIUM_SKIN_TONE' => "\u{1F9DD}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_ELF_MEDIUM_DARK_SKIN_TONE' => "\u{1F9DD}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_ELF_DARK_SKIN_TONE' => "\u{1F9DD}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_GENIE' => "\u{1F9DE}", 'CHARACTER_WOMAN_GENIE' => "\u{1F9DE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MAN_GENIE' => "\u{1F9DE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_ZOMBIE' => "\u{1F9DF}", 'CHARACTER_WOMAN_ZOMBIE' => "\u{1F9DF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MAN_ZOMBIE' => "\u{1F9DF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_PERSON_FROWNING' => "\u{1F64D}", 'CHARACTER_PERSON_FROWNING_LIGHT_SKIN_TONE' => "\u{1F64D}\u{1F3FB}", 'CHARACTER_PERSON_FROWNING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F64D}\u{1F3FC}", 'CHARACTER_PERSON_FROWNING_MEDIUM_SKIN_TONE' => "\u{1F64D}\u{1F3FD}", 'CHARACTER_PERSON_FROWNING_MEDIUM_DARK_SKIN_TONE' => "\u{1F64D}\u{1F3FE}", 'CHARACTER_PERSON_FROWNING_DARK_SKIN_TONE' => "\u{1F64D}\u{1F3FF}", 'CHARACTER_MAN_FROWNING' => "\u{1F64D}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_FROWNING_LIGHT_SKIN_TONE' => "\u{1F64D}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_FROWNING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F64D}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_FROWNING_MEDIUM_SKIN_TONE' => "\u{1F64D}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_FROWNING_MEDIUM_DARK_SKIN_TONE' => "\u{1F64D}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_FROWNING_DARK_SKIN_TONE' => "\u{1F64D}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_FROWNING' => "\u{1F64D}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_FROWNING_LIGHT_SKIN_TONE' => "\u{1F64D}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_FROWNING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F64D}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_FROWNING_MEDIUM_SKIN_TONE' => "\u{1F64D}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_FROWNING_MEDIUM_DARK_SKIN_TONE' => "\u{1F64D}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_FROWNING_DARK_SKIN_TONE' => "\u{1F64D}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_POUTING' => "\u{1F64E}", 'CHARACTER_PERSON_POUTING_LIGHT_SKIN_TONE' => "\u{1F64E}\u{1F3FB}", 'CHARACTER_PERSON_POUTING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F64E}\u{1F3FC}", 'CHARACTER_PERSON_POUTING_MEDIUM_SKIN_TONE' => "\u{1F64E}\u{1F3FD}", 'CHARACTER_PERSON_POUTING_MEDIUM_DARK_SKIN_TONE' => "\u{1F64E}\u{1F3FE}", 'CHARACTER_PERSON_POUTING_DARK_SKIN_TONE' => "\u{1F64E}\u{1F3FF}", 'CHARACTER_MAN_POUTING' => "\u{1F64E}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_POUTING_LIGHT_SKIN_TONE' => "\u{1F64E}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_POUTING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F64E}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_POUTING_MEDIUM_SKIN_TONE' => "\u{1F64E}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_POUTING_MEDIUM_DARK_SKIN_TONE' => "\u{1F64E}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_POUTING_DARK_SKIN_TONE' => "\u{1F64E}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_POUTING' => "\u{1F64E}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_POUTING_LIGHT_SKIN_TONE' => "\u{1F64E}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_POUTING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F64E}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_POUTING_MEDIUM_SKIN_TONE' => "\u{1F64E}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_POUTING_MEDIUM_DARK_SKIN_TONE' => "\u{1F64E}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_POUTING_DARK_SKIN_TONE' => "\u{1F64E}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_GESTURING_NO' => "\u{1F645}", 'CHARACTER_PERSON_GESTURING_NO_LIGHT_SKIN_TONE' => "\u{1F645}\u{1F3FB}", 'CHARACTER_PERSON_GESTURING_NO_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F645}\u{1F3FC}", 'CHARACTER_PERSON_GESTURING_NO_MEDIUM_SKIN_TONE' => "\u{1F645}\u{1F3FD}", 'CHARACTER_PERSON_GESTURING_NO_MEDIUM_DARK_SKIN_TONE' => "\u{1F645}\u{1F3FE}", 'CHARACTER_PERSON_GESTURING_NO_DARK_SKIN_TONE' => "\u{1F645}\u{1F3FF}", 'CHARACTER_MAN_GESTURING_NO' => "\u{1F645}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GESTURING_NO_LIGHT_SKIN_TONE' => "\u{1F645}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GESTURING_NO_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F645}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GESTURING_NO_MEDIUM_SKIN_TONE' => "\u{1F645}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GESTURING_NO_MEDIUM_DARK_SKIN_TONE' => "\u{1F645}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GESTURING_NO_DARK_SKIN_TONE' => "\u{1F645}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_GESTURING_NO' => "\u{1F645}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GESTURING_NO_LIGHT_SKIN_TONE' => "\u{1F645}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GESTURING_NO_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F645}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GESTURING_NO_MEDIUM_SKIN_TONE' => "\u{1F645}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GESTURING_NO_MEDIUM_DARK_SKIN_TONE' => "\u{1F645}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GESTURING_NO_DARK_SKIN_TONE' => "\u{1F645}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_GESTURING_OK' => "\u{1F646}", 'CHARACTER_PERSON_GESTURING_OK_LIGHT_SKIN_TONE' => "\u{1F646}\u{1F3FB}", 'CHARACTER_PERSON_GESTURING_OK_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F646}\u{1F3FC}", 'CHARACTER_PERSON_GESTURING_OK_MEDIUM_SKIN_TONE' => "\u{1F646}\u{1F3FD}", 'CHARACTER_PERSON_GESTURING_OK_MEDIUM_DARK_SKIN_TONE' => "\u{1F646}\u{1F3FE}", 'CHARACTER_PERSON_GESTURING_OK_DARK_SKIN_TONE' => "\u{1F646}\u{1F3FF}", 'CHARACTER_MAN_GESTURING_OK' => "\u{1F646}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GESTURING_OK_LIGHT_SKIN_TONE' => "\u{1F646}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GESTURING_OK_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F646}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GESTURING_OK_MEDIUM_SKIN_TONE' => "\u{1F646}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GESTURING_OK_MEDIUM_DARK_SKIN_TONE' => "\u{1F646}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GESTURING_OK_DARK_SKIN_TONE' => "\u{1F646}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_GESTURING_OK' => "\u{1F646}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GESTURING_OK_LIGHT_SKIN_TONE' => "\u{1F646}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GESTURING_OK_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F646}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GESTURING_OK_MEDIUM_SKIN_TONE' => "\u{1F646}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GESTURING_OK_MEDIUM_DARK_SKIN_TONE' => "\u{1F646}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GESTURING_OK_DARK_SKIN_TONE' => "\u{1F646}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_TIPPING_HAND' => "\u{1F481}", 'CHARACTER_PERSON_TIPPING_HAND_LIGHT_SKIN_TONE' => "\u{1F481}\u{1F3FB}", 'CHARACTER_PERSON_TIPPING_HAND_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F481}\u{1F3FC}", 'CHARACTER_PERSON_TIPPING_HAND_MEDIUM_SKIN_TONE' => "\u{1F481}\u{1F3FD}", 'CHARACTER_PERSON_TIPPING_HAND_MEDIUM_DARK_SKIN_TONE' => "\u{1F481}\u{1F3FE}", 'CHARACTER_PERSON_TIPPING_HAND_DARK_SKIN_TONE' => "\u{1F481}\u{1F3FF}", 'CHARACTER_MAN_TIPPING_HAND' => "\u{1F481}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_TIPPING_HAND_LIGHT_SKIN_TONE' => "\u{1F481}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_TIPPING_HAND_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F481}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_TIPPING_HAND_MEDIUM_SKIN_TONE' => "\u{1F481}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_TIPPING_HAND_MEDIUM_DARK_SKIN_TONE' => "\u{1F481}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_TIPPING_HAND_DARK_SKIN_TONE' => "\u{1F481}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_TIPPING_HAND' => "\u{1F481}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_TIPPING_HAND_LIGHT_SKIN_TONE' => "\u{1F481}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_TIPPING_HAND_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F481}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_TIPPING_HAND_MEDIUM_SKIN_TONE' => "\u{1F481}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_TIPPING_HAND_MEDIUM_DARK_SKIN_TONE' => "\u{1F481}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_TIPPING_HAND_DARK_SKIN_TONE' => "\u{1F481}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_RAISING_HAND' => "\u{1F64B}", 'CHARACTER_PERSON_RAISING_HAND_LIGHT_SKIN_TONE' => "\u{1F64B}\u{1F3FB}", 'CHARACTER_PERSON_RAISING_HAND_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F64B}\u{1F3FC}", 'CHARACTER_PERSON_RAISING_HAND_MEDIUM_SKIN_TONE' => "\u{1F64B}\u{1F3FD}", 'CHARACTER_PERSON_RAISING_HAND_MEDIUM_DARK_SKIN_TONE' => "\u{1F64B}\u{1F3FE}", 'CHARACTER_PERSON_RAISING_HAND_DARK_SKIN_TONE' => "\u{1F64B}\u{1F3FF}", 'CHARACTER_MAN_RAISING_HAND' => "\u{1F64B}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_RAISING_HAND_LIGHT_SKIN_TONE' => "\u{1F64B}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_RAISING_HAND_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F64B}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_RAISING_HAND_MEDIUM_SKIN_TONE' => "\u{1F64B}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_RAISING_HAND_MEDIUM_DARK_SKIN_TONE' => "\u{1F64B}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_RAISING_HAND_DARK_SKIN_TONE' => "\u{1F64B}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_RAISING_HAND' => "\u{1F64B}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_RAISING_HAND_LIGHT_SKIN_TONE' => "\u{1F64B}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_RAISING_HAND_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F64B}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_RAISING_HAND_MEDIUM_SKIN_TONE' => "\u{1F64B}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_RAISING_HAND_MEDIUM_DARK_SKIN_TONE' => "\u{1F64B}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_RAISING_HAND_DARK_SKIN_TONE' => "\u{1F64B}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_BOWING' => "\u{1F647}", 'CHARACTER_PERSON_BOWING_LIGHT_SKIN_TONE' => "\u{1F647}\u{1F3FB}", 'CHARACTER_PERSON_BOWING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F647}\u{1F3FC}", 'CHARACTER_PERSON_BOWING_MEDIUM_SKIN_TONE' => "\u{1F647}\u{1F3FD}", 'CHARACTER_PERSON_BOWING_MEDIUM_DARK_SKIN_TONE' => "\u{1F647}\u{1F3FE}", 'CHARACTER_PERSON_BOWING_DARK_SKIN_TONE' => "\u{1F647}\u{1F3FF}", 'CHARACTER_MAN_BOWING' => "\u{1F647}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_BOWING_LIGHT_SKIN_TONE' => "\u{1F647}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_BOWING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F647}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_BOWING_MEDIUM_SKIN_TONE' => "\u{1F647}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_BOWING_MEDIUM_DARK_SKIN_TONE' => "\u{1F647}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_BOWING_DARK_SKIN_TONE' => "\u{1F647}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_BOWING' => "\u{1F647}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_BOWING_LIGHT_SKIN_TONE' => "\u{1F647}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_BOWING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F647}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_BOWING_MEDIUM_SKIN_TONE' => "\u{1F647}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_BOWING_MEDIUM_DARK_SKIN_TONE' => "\u{1F647}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_BOWING_DARK_SKIN_TONE' => "\u{1F647}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_FACEPALMING' => "\u{1F926}", 'CHARACTER_PERSON_FACEPALMING_LIGHT_SKIN_TONE' => "\u{1F926}\u{1F3FB}", 'CHARACTER_PERSON_FACEPALMING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F926}\u{1F3FC}", 'CHARACTER_PERSON_FACEPALMING_MEDIUM_SKIN_TONE' => "\u{1F926}\u{1F3FD}", 'CHARACTER_PERSON_FACEPALMING_MEDIUM_DARK_SKIN_TONE' => "\u{1F926}\u{1F3FE}", 'CHARACTER_PERSON_FACEPALMING_DARK_SKIN_TONE' => "\u{1F926}\u{1F3FF}", 'CHARACTER_MAN_FACEPALMING' => "\u{1F926}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_FACEPALMING_LIGHT_SKIN_TONE' => "\u{1F926}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_FACEPALMING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F926}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_FACEPALMING_MEDIUM_SKIN_TONE' => "\u{1F926}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_FACEPALMING_MEDIUM_DARK_SKIN_TONE' => "\u{1F926}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_FACEPALMING_DARK_SKIN_TONE' => "\u{1F926}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_FACEPALMING' => "\u{1F926}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_FACEPALMING_LIGHT_SKIN_TONE' => "\u{1F926}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_FACEPALMING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F926}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_FACEPALMING_MEDIUM_SKIN_TONE' => "\u{1F926}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_FACEPALMING_MEDIUM_DARK_SKIN_TONE' => "\u{1F926}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_FACEPALMING_DARK_SKIN_TONE' => "\u{1F926}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_SHRUGGING' => "\u{1F937}", 'CHARACTER_PERSON_SHRUGGING_LIGHT_SKIN_TONE' => "\u{1F937}\u{1F3FB}", 'CHARACTER_PERSON_SHRUGGING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F937}\u{1F3FC}", 'CHARACTER_PERSON_SHRUGGING_MEDIUM_SKIN_TONE' => "\u{1F937}\u{1F3FD}", 'CHARACTER_PERSON_SHRUGGING_MEDIUM_DARK_SKIN_TONE' => "\u{1F937}\u{1F3FE}", 'CHARACTER_PERSON_SHRUGGING_DARK_SKIN_TONE' => "\u{1F937}\u{1F3FF}", 'CHARACTER_MAN_SHRUGGING' => "\u{1F937}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SHRUGGING_LIGHT_SKIN_TONE' => "\u{1F937}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SHRUGGING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F937}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SHRUGGING_MEDIUM_SKIN_TONE' => "\u{1F937}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SHRUGGING_MEDIUM_DARK_SKIN_TONE' => "\u{1F937}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SHRUGGING_DARK_SKIN_TONE' => "\u{1F937}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_SHRUGGING' => "\u{1F937}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SHRUGGING_LIGHT_SKIN_TONE' => "\u{1F937}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SHRUGGING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F937}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SHRUGGING_MEDIUM_SKIN_TONE' => "\u{1F937}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SHRUGGING_MEDIUM_DARK_SKIN_TONE' => "\u{1F937}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SHRUGGING_DARK_SKIN_TONE' => "\u{1F937}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_GETTING_MASSAGE' => "\u{1F486}", 'CHARACTER_PERSON_GETTING_MASSAGE_LIGHT_SKIN_TONE' => "\u{1F486}\u{1F3FB}", 'CHARACTER_PERSON_GETTING_MASSAGE_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F486}\u{1F3FC}", 'CHARACTER_PERSON_GETTING_MASSAGE_MEDIUM_SKIN_TONE' => "\u{1F486}\u{1F3FD}", 'CHARACTER_PERSON_GETTING_MASSAGE_MEDIUM_DARK_SKIN_TONE' => "\u{1F486}\u{1F3FE}", 'CHARACTER_PERSON_GETTING_MASSAGE_DARK_SKIN_TONE' => "\u{1F486}\u{1F3FF}", 'CHARACTER_MAN_GETTING_MASSAGE' => "\u{1F486}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GETTING_MASSAGE_LIGHT_SKIN_TONE' => "\u{1F486}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GETTING_MASSAGE_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F486}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GETTING_MASSAGE_MEDIUM_SKIN_TONE' => "\u{1F486}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GETTING_MASSAGE_MEDIUM_DARK_SKIN_TONE' => "\u{1F486}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GETTING_MASSAGE_DARK_SKIN_TONE' => "\u{1F486}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_GETTING_MASSAGE' => "\u{1F486}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GETTING_MASSAGE_LIGHT_SKIN_TONE' => "\u{1F486}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GETTING_MASSAGE_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F486}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GETTING_MASSAGE_MEDIUM_SKIN_TONE' => "\u{1F486}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GETTING_MASSAGE_MEDIUM_DARK_SKIN_TONE' => "\u{1F486}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GETTING_MASSAGE_DARK_SKIN_TONE' => "\u{1F486}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_GETTING_HAIRCUT' => "\u{1F487}", 'CHARACTER_PERSON_GETTING_HAIRCUT_LIGHT_SKIN_TONE' => "\u{1F487}\u{1F3FB}", 'CHARACTER_PERSON_GETTING_HAIRCUT_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F487}\u{1F3FC}", 'CHARACTER_PERSON_GETTING_HAIRCUT_MEDIUM_SKIN_TONE' => "\u{1F487}\u{1F3FD}", 'CHARACTER_PERSON_GETTING_HAIRCUT_MEDIUM_DARK_SKIN_TONE' => "\u{1F487}\u{1F3FE}", 'CHARACTER_PERSON_GETTING_HAIRCUT_DARK_SKIN_TONE' => "\u{1F487}\u{1F3FF}", 'CHARACTER_MAN_GETTING_HAIRCUT' => "\u{1F487}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GETTING_HAIRCUT_LIGHT_SKIN_TONE' => "\u{1F487}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GETTING_HAIRCUT_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F487}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GETTING_HAIRCUT_MEDIUM_SKIN_TONE' => "\u{1F487}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GETTING_HAIRCUT_MEDIUM_DARK_SKIN_TONE' => "\u{1F487}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GETTING_HAIRCUT_DARK_SKIN_TONE' => "\u{1F487}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_GETTING_HAIRCUT' => "\u{1F487}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GETTING_HAIRCUT_LIGHT_SKIN_TONE' => "\u{1F487}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GETTING_HAIRCUT_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F487}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GETTING_HAIRCUT_MEDIUM_SKIN_TONE' => "\u{1F487}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GETTING_HAIRCUT_MEDIUM_DARK_SKIN_TONE' => "\u{1F487}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GETTING_HAIRCUT_DARK_SKIN_TONE' => "\u{1F487}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_WALKING' => "\u{1F6B6}", 'CHARACTER_PERSON_WALKING_LIGHT_SKIN_TONE' => "\u{1F6B6}\u{1F3FB}", 'CHARACTER_PERSON_WALKING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F6B6}\u{1F3FC}", 'CHARACTER_PERSON_WALKING_MEDIUM_SKIN_TONE' => "\u{1F6B6}\u{1F3FD}", 'CHARACTER_PERSON_WALKING_MEDIUM_DARK_SKIN_TONE' => "\u{1F6B6}\u{1F3FE}", 'CHARACTER_PERSON_WALKING_DARK_SKIN_TONE' => "\u{1F6B6}\u{1F3FF}", 'CHARACTER_MAN_WALKING' => "\u{1F6B6}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_WALKING_LIGHT_SKIN_TONE' => "\u{1F6B6}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_WALKING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F6B6}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_WALKING_MEDIUM_SKIN_TONE' => "\u{1F6B6}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_WALKING_MEDIUM_DARK_SKIN_TONE' => "\u{1F6B6}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_WALKING_DARK_SKIN_TONE' => "\u{1F6B6}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_WALKING' => "\u{1F6B6}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_WALKING_LIGHT_SKIN_TONE' => "\u{1F6B6}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_WALKING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F6B6}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_WALKING_MEDIUM_SKIN_TONE' => "\u{1F6B6}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_WALKING_MEDIUM_DARK_SKIN_TONE' => "\u{1F6B6}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_WALKING_DARK_SKIN_TONE' => "\u{1F6B6}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_RUNNING' => "\u{1F3C3}", 'CHARACTER_PERSON_RUNNING_LIGHT_SKIN_TONE' => "\u{1F3C3}\u{1F3FB}", 'CHARACTER_PERSON_RUNNING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F3C3}\u{1F3FC}", 'CHARACTER_PERSON_RUNNING_MEDIUM_SKIN_TONE' => "\u{1F3C3}\u{1F3FD}", 'CHARACTER_PERSON_RUNNING_MEDIUM_DARK_SKIN_TONE' => "\u{1F3C3}\u{1F3FE}", 'CHARACTER_PERSON_RUNNING_DARK_SKIN_TONE' => "\u{1F3C3}\u{1F3FF}", 'CHARACTER_MAN_RUNNING' => "\u{1F3C3}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_RUNNING_LIGHT_SKIN_TONE' => "\u{1F3C3}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_RUNNING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F3C3}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_RUNNING_MEDIUM_SKIN_TONE' => "\u{1F3C3}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_RUNNING_MEDIUM_DARK_SKIN_TONE' => "\u{1F3C3}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_RUNNING_DARK_SKIN_TONE' => "\u{1F3C3}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_RUNNING' => "\u{1F3C3}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_RUNNING_LIGHT_SKIN_TONE' => "\u{1F3C3}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_RUNNING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F3C3}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_RUNNING_MEDIUM_SKIN_TONE' => "\u{1F3C3}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_RUNNING_MEDIUM_DARK_SKIN_TONE' => "\u{1F3C3}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_RUNNING_DARK_SKIN_TONE' => "\u{1F3C3}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_DANCING' => "\u{1F483}", 'CHARACTER_WOMAN_DANCING_LIGHT_SKIN_TONE' => "\u{1F483}\u{1F3FB}", 'CHARACTER_WOMAN_DANCING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F483}\u{1F3FC}", 'CHARACTER_WOMAN_DANCING_MEDIUM_SKIN_TONE' => "\u{1F483}\u{1F3FD}", 'CHARACTER_WOMAN_DANCING_MEDIUM_DARK_SKIN_TONE' => "\u{1F483}\u{1F3FE}", 'CHARACTER_WOMAN_DANCING_DARK_SKIN_TONE' => "\u{1F483}\u{1F3FF}", 'CHARACTER_MAN_DANCING' => "\u{1F57A}", 'CHARACTER_MAN_DANCING_LIGHT_SKIN_TONE' => "\u{1F57A}\u{1F3FB}", 'CHARACTER_MAN_DANCING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F57A}\u{1F3FC}", 'CHARACTER_MAN_DANCING_MEDIUM_SKIN_TONE' => "\u{1F57A}\u{1F3FD}", 'CHARACTER_MAN_DANCING_MEDIUM_DARK_SKIN_TONE' => "\u{1F57A}\u{1F3FE}", 'CHARACTER_MAN_DANCING_DARK_SKIN_TONE' => "\u{1F57A}\u{1F3FF}", 'CHARACTER_PEOPLE_WITH_BUNNY_EARS' => "\u{1F46F}", 'CHARACTER_MEN_WITH_BUNNY_EARS' => "\u{1F46F}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMEN_WITH_BUNNY_EARS' => "\u{1F46F}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_IN_STEAMY_ROOM' => "\u{1F9D6}", 'CHARACTER_PERSON_IN_STEAMY_ROOM_LIGHT_SKIN_TONE' => "\u{1F9D6}\u{1F3FB}", 'CHARACTER_PERSON_IN_STEAMY_ROOM_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9D6}\u{1F3FC}", 'CHARACTER_PERSON_IN_STEAMY_ROOM_MEDIUM_SKIN_TONE' => "\u{1F9D6}\u{1F3FD}", 'CHARACTER_PERSON_IN_STEAMY_ROOM_MEDIUM_DARK_SKIN_TONE' => "\u{1F9D6}\u{1F3FE}", 'CHARACTER_PERSON_IN_STEAMY_ROOM_DARK_SKIN_TONE' => "\u{1F9D6}\u{1F3FF}", 'CHARACTER_WOMAN_IN_STEAMY_ROOM' => "\u{1F9D6}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_IN_STEAMY_ROOM_LIGHT_SKIN_TONE' => "\u{1F9D6}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_IN_STEAMY_ROOM_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9D6}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_IN_STEAMY_ROOM_MEDIUM_SKIN_TONE' => "\u{1F9D6}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_IN_STEAMY_ROOM_MEDIUM_DARK_SKIN_TONE' => "\u{1F9D6}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_IN_STEAMY_ROOM_DARK_SKIN_TONE' => "\u{1F9D6}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MAN_IN_STEAMY_ROOM' => "\u{1F9D6}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_IN_STEAMY_ROOM_LIGHT_SKIN_TONE' => "\u{1F9D6}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_IN_STEAMY_ROOM_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9D6}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_IN_STEAMY_ROOM_MEDIUM_SKIN_TONE' => "\u{1F9D6}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_IN_STEAMY_ROOM_MEDIUM_DARK_SKIN_TONE' => "\u{1F9D6}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_IN_STEAMY_ROOM_DARK_SKIN_TONE' => "\u{1F9D6}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_PERSON_CLIMBING' => "\u{1F9D7}", 'CHARACTER_PERSON_CLIMBING_LIGHT_SKIN_TONE' => "\u{1F9D7}\u{1F3FB}", 'CHARACTER_PERSON_CLIMBING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9D7}\u{1F3FC}", 'CHARACTER_PERSON_CLIMBING_MEDIUM_SKIN_TONE' => "\u{1F9D7}\u{1F3FD}", 'CHARACTER_PERSON_CLIMBING_MEDIUM_DARK_SKIN_TONE' => "\u{1F9D7}\u{1F3FE}", 'CHARACTER_PERSON_CLIMBING_DARK_SKIN_TONE' => "\u{1F9D7}\u{1F3FF}", 'CHARACTER_WOMAN_CLIMBING' => "\u{1F9D7}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_CLIMBING_LIGHT_SKIN_TONE' => "\u{1F9D7}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_CLIMBING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9D7}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_CLIMBING_MEDIUM_SKIN_TONE' => "\u{1F9D7}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_CLIMBING_MEDIUM_DARK_SKIN_TONE' => "\u{1F9D7}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_CLIMBING_DARK_SKIN_TONE' => "\u{1F9D7}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MAN_CLIMBING' => "\u{1F9D7}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_CLIMBING_LIGHT_SKIN_TONE' => "\u{1F9D7}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_CLIMBING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9D7}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_CLIMBING_MEDIUM_SKIN_TONE' => "\u{1F9D7}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_CLIMBING_MEDIUM_DARK_SKIN_TONE' => "\u{1F9D7}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_CLIMBING_DARK_SKIN_TONE' => "\u{1F9D7}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_PERSON_IN_LOTUS_POSITION' => "\u{1F9D8}", 'CHARACTER_PERSON_IN_LOTUS_POSITION_LIGHT_SKIN_TONE' => "\u{1F9D8}\u{1F3FB}", 'CHARACTER_PERSON_IN_LOTUS_POSITION_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9D8}\u{1F3FC}", 'CHARACTER_PERSON_IN_LOTUS_POSITION_MEDIUM_SKIN_TONE' => "\u{1F9D8}\u{1F3FD}", 'CHARACTER_PERSON_IN_LOTUS_POSITION_MEDIUM_DARK_SKIN_TONE' => "\u{1F9D8}\u{1F3FE}", 'CHARACTER_PERSON_IN_LOTUS_POSITION_DARK_SKIN_TONE' => "\u{1F9D8}\u{1F3FF}", 'CHARACTER_WOMAN_IN_LOTUS_POSITION' => "\u{1F9D8}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_IN_LOTUS_POSITION_LIGHT_SKIN_TONE' => "\u{1F9D8}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_IN_LOTUS_POSITION_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9D8}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_IN_LOTUS_POSITION_MEDIUM_SKIN_TONE' => "\u{1F9D8}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_IN_LOTUS_POSITION_MEDIUM_DARK_SKIN_TONE' => "\u{1F9D8}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_IN_LOTUS_POSITION_DARK_SKIN_TONE' => "\u{1F9D8}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MAN_IN_LOTUS_POSITION' => "\u{1F9D8}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_IN_LOTUS_POSITION_LIGHT_SKIN_TONE' => "\u{1F9D8}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_IN_LOTUS_POSITION_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9D8}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_IN_LOTUS_POSITION_MEDIUM_SKIN_TONE' => "\u{1F9D8}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_IN_LOTUS_POSITION_MEDIUM_DARK_SKIN_TONE' => "\u{1F9D8}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_IN_LOTUS_POSITION_DARK_SKIN_TONE' => "\u{1F9D8}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_PERSON_TAKING_BATH' => "\u{1F6C0}", 'CHARACTER_PERSON_TAKING_BATH_LIGHT_SKIN_TONE' => "\u{1F6C0}\u{1F3FB}", 'CHARACTER_PERSON_TAKING_BATH_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F6C0}\u{1F3FC}", 'CHARACTER_PERSON_TAKING_BATH_MEDIUM_SKIN_TONE' => "\u{1F6C0}\u{1F3FD}", 'CHARACTER_PERSON_TAKING_BATH_MEDIUM_DARK_SKIN_TONE' => "\u{1F6C0}\u{1F3FE}", 'CHARACTER_PERSON_TAKING_BATH_DARK_SKIN_TONE' => "\u{1F6C0}\u{1F3FF}", 'CHARACTER_PERSON_IN_BED' => "\u{1F6CC}", 'CHARACTER_PERSON_IN_BED_LIGHT_SKIN_TONE' => "\u{1F6CC}\u{1F3FB}", 'CHARACTER_PERSON_IN_BED_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F6CC}\u{1F3FC}", 'CHARACTER_PERSON_IN_BED_MEDIUM_SKIN_TONE' => "\u{1F6CC}\u{1F3FD}", 'CHARACTER_PERSON_IN_BED_MEDIUM_DARK_SKIN_TONE' => "\u{1F6CC}\u{1F3FE}", 'CHARACTER_PERSON_IN_BED_DARK_SKIN_TONE' => "\u{1F6CC}\u{1F3FF}", 'CHARACTER_MAN_IN_SUIT_LEVITATING' => "\u{1F574}\u{FE0F}", 'CHARACTER_MAN_IN_SUIT_LEVITATING_LIGHT_SKIN_TONE' => "\u{1F574}\u{1F3FB}", 'CHARACTER_MAN_IN_SUIT_LEVITATING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F574}\u{1F3FC}", 'CHARACTER_MAN_IN_SUIT_LEVITATING_MEDIUM_SKIN_TONE' => "\u{1F574}\u{1F3FD}", 'CHARACTER_MAN_IN_SUIT_LEVITATING_MEDIUM_DARK_SKIN_TONE' => "\u{1F574}\u{1F3FE}", 'CHARACTER_MAN_IN_SUIT_LEVITATING_DARK_SKIN_TONE' => "\u{1F574}\u{1F3FF}", 'CHARACTER_SPEAKING_HEAD' => "\u{1F5E3}\u{FE0F}", 'CHARACTER_BUST_IN_SILHOUETTE' => "\u{1F464}", 'CHARACTER_BUSTS_IN_SILHOUETTE' => "\u{1F465}", 'CHARACTER_PERSON_FENCING' => "\u{1F93A}", 'CHARACTER_HORSE_RACING' => "\u{1F3C7}", 'CHARACTER_HORSE_RACING_LIGHT_SKIN_TONE' => "\u{1F3C7}\u{1F3FB}", 'CHARACTER_HORSE_RACING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F3C7}\u{1F3FC}", 'CHARACTER_HORSE_RACING_MEDIUM_SKIN_TONE' => "\u{1F3C7}\u{1F3FD}", 'CHARACTER_HORSE_RACING_MEDIUM_DARK_SKIN_TONE' => "\u{1F3C7}\u{1F3FE}", 'CHARACTER_HORSE_RACING_DARK_SKIN_TONE' => "\u{1F3C7}\u{1F3FF}", 'CHARACTER_SKIER' => "\u{26F7}\u{FE0F}", 'CHARACTER_SNOWBOARDER' => "\u{1F3C2}", 'CHARACTER_SNOWBOARDER_LIGHT_SKIN_TONE' => "\u{1F3C2}\u{1F3FB}", 'CHARACTER_SNOWBOARDER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F3C2}\u{1F3FC}", 'CHARACTER_SNOWBOARDER_MEDIUM_SKIN_TONE' => "\u{1F3C2}\u{1F3FD}", 'CHARACTER_SNOWBOARDER_MEDIUM_DARK_SKIN_TONE' => "\u{1F3C2}\u{1F3FE}", 'CHARACTER_SNOWBOARDER_DARK_SKIN_TONE' => "\u{1F3C2}\u{1F3FF}", 'CHARACTER_PERSON_GOLFING' => "\u{1F3CC}\u{FE0F}", 'CHARACTER_PERSON_GOLFING_LIGHT_SKIN_TONE' => "\u{1F3CC}\u{1F3FB}", 'CHARACTER_PERSON_GOLFING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F3CC}\u{1F3FC}", 'CHARACTER_PERSON_GOLFING_MEDIUM_SKIN_TONE' => "\u{1F3CC}\u{1F3FD}", 'CHARACTER_PERSON_GOLFING_MEDIUM_DARK_SKIN_TONE' => "\u{1F3CC}\u{1F3FE}", 'CHARACTER_PERSON_GOLFING_DARK_SKIN_TONE' => "\u{1F3CC}\u{1F3FF}", 'CHARACTER_MAN_GOLFING' => "\u{1F3CC}\u{FE0F}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GOLFING_LIGHT_SKIN_TONE' => "\u{1F3CC}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GOLFING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F3CC}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GOLFING_MEDIUM_SKIN_TONE' => "\u{1F3CC}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GOLFING_MEDIUM_DARK_SKIN_TONE' => "\u{1F3CC}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_GOLFING_DARK_SKIN_TONE' => "\u{1F3CC}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_GOLFING' => "\u{1F3CC}\u{FE0F}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GOLFING_LIGHT_SKIN_TONE' => "\u{1F3CC}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GOLFING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F3CC}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GOLFING_MEDIUM_SKIN_TONE' => "\u{1F3CC}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GOLFING_MEDIUM_DARK_SKIN_TONE' => "\u{1F3CC}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_GOLFING_DARK_SKIN_TONE' => "\u{1F3CC}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_SURFING' => "\u{1F3C4}", 'CHARACTER_PERSON_SURFING_LIGHT_SKIN_TONE' => "\u{1F3C4}\u{1F3FB}", 'CHARACTER_PERSON_SURFING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F3C4}\u{1F3FC}", 'CHARACTER_PERSON_SURFING_MEDIUM_SKIN_TONE' => "\u{1F3C4}\u{1F3FD}", 'CHARACTER_PERSON_SURFING_MEDIUM_DARK_SKIN_TONE' => "\u{1F3C4}\u{1F3FE}", 'CHARACTER_PERSON_SURFING_DARK_SKIN_TONE' => "\u{1F3C4}\u{1F3FF}", 'CHARACTER_MAN_SURFING' => "\u{1F3C4}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SURFING_LIGHT_SKIN_TONE' => "\u{1F3C4}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SURFING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F3C4}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SURFING_MEDIUM_SKIN_TONE' => "\u{1F3C4}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SURFING_MEDIUM_DARK_SKIN_TONE' => "\u{1F3C4}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SURFING_DARK_SKIN_TONE' => "\u{1F3C4}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_SURFING' => "\u{1F3C4}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SURFING_LIGHT_SKIN_TONE' => "\u{1F3C4}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SURFING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F3C4}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SURFING_MEDIUM_SKIN_TONE' => "\u{1F3C4}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SURFING_MEDIUM_DARK_SKIN_TONE' => "\u{1F3C4}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SURFING_DARK_SKIN_TONE' => "\u{1F3C4}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_ROWING_BOAT' => "\u{1F6A3}", 'CHARACTER_PERSON_ROWING_BOAT_LIGHT_SKIN_TONE' => "\u{1F6A3}\u{1F3FB}", 'CHARACTER_PERSON_ROWING_BOAT_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F6A3}\u{1F3FC}", 'CHARACTER_PERSON_ROWING_BOAT_MEDIUM_SKIN_TONE' => "\u{1F6A3}\u{1F3FD}", 'CHARACTER_PERSON_ROWING_BOAT_MEDIUM_DARK_SKIN_TONE' => "\u{1F6A3}\u{1F3FE}", 'CHARACTER_PERSON_ROWING_BOAT_DARK_SKIN_TONE' => "\u{1F6A3}\u{1F3FF}", 'CHARACTER_MAN_ROWING_BOAT' => "\u{1F6A3}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_ROWING_BOAT_LIGHT_SKIN_TONE' => "\u{1F6A3}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_ROWING_BOAT_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F6A3}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_ROWING_BOAT_MEDIUM_SKIN_TONE' => "\u{1F6A3}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_ROWING_BOAT_MEDIUM_DARK_SKIN_TONE' => "\u{1F6A3}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_ROWING_BOAT_DARK_SKIN_TONE' => "\u{1F6A3}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_ROWING_BOAT' => "\u{1F6A3}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_ROWING_BOAT_LIGHT_SKIN_TONE' => "\u{1F6A3}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_ROWING_BOAT_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F6A3}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_ROWING_BOAT_MEDIUM_SKIN_TONE' => "\u{1F6A3}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_ROWING_BOAT_MEDIUM_DARK_SKIN_TONE' => "\u{1F6A3}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_ROWING_BOAT_DARK_SKIN_TONE' => "\u{1F6A3}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_SWIMMING' => "\u{1F3CA}", 'CHARACTER_PERSON_SWIMMING_LIGHT_SKIN_TONE' => "\u{1F3CA}\u{1F3FB}", 'CHARACTER_PERSON_SWIMMING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F3CA}\u{1F3FC}", 'CHARACTER_PERSON_SWIMMING_MEDIUM_SKIN_TONE' => "\u{1F3CA}\u{1F3FD}", 'CHARACTER_PERSON_SWIMMING_MEDIUM_DARK_SKIN_TONE' => "\u{1F3CA}\u{1F3FE}", 'CHARACTER_PERSON_SWIMMING_DARK_SKIN_TONE' => "\u{1F3CA}\u{1F3FF}", 'CHARACTER_MAN_SWIMMING' => "\u{1F3CA}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SWIMMING_LIGHT_SKIN_TONE' => "\u{1F3CA}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SWIMMING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F3CA}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SWIMMING_MEDIUM_SKIN_TONE' => "\u{1F3CA}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SWIMMING_MEDIUM_DARK_SKIN_TONE' => "\u{1F3CA}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_SWIMMING_DARK_SKIN_TONE' => "\u{1F3CA}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_SWIMMING' => "\u{1F3CA}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SWIMMING_LIGHT_SKIN_TONE' => "\u{1F3CA}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SWIMMING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F3CA}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SWIMMING_MEDIUM_SKIN_TONE' => "\u{1F3CA}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SWIMMING_MEDIUM_DARK_SKIN_TONE' => "\u{1F3CA}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_SWIMMING_DARK_SKIN_TONE' => "\u{1F3CA}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_BOUNCING_BALL' => "\u{26F9}\u{FE0F}", 'CHARACTER_PERSON_BOUNCING_BALL_LIGHT_SKIN_TONE' => "\u{26F9}\u{1F3FB}", 'CHARACTER_PERSON_BOUNCING_BALL_MEDIUM_LIGHT_SKIN_TONE' => "\u{26F9}\u{1F3FC}", 'CHARACTER_PERSON_BOUNCING_BALL_MEDIUM_SKIN_TONE' => "\u{26F9}\u{1F3FD}", 'CHARACTER_PERSON_BOUNCING_BALL_MEDIUM_DARK_SKIN_TONE' => "\u{26F9}\u{1F3FE}", 'CHARACTER_PERSON_BOUNCING_BALL_DARK_SKIN_TONE' => "\u{26F9}\u{1F3FF}", 'CHARACTER_MAN_BOUNCING_BALL' => "\u{26F9}\u{FE0F}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_BOUNCING_BALL_LIGHT_SKIN_TONE' => "\u{26F9}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_BOUNCING_BALL_MEDIUM_LIGHT_SKIN_TONE' => "\u{26F9}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_BOUNCING_BALL_MEDIUM_SKIN_TONE' => "\u{26F9}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_BOUNCING_BALL_MEDIUM_DARK_SKIN_TONE' => "\u{26F9}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_BOUNCING_BALL_DARK_SKIN_TONE' => "\u{26F9}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_BOUNCING_BALL' => "\u{26F9}\u{FE0F}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_BOUNCING_BALL_LIGHT_SKIN_TONE' => "\u{26F9}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_BOUNCING_BALL_MEDIUM_LIGHT_SKIN_TONE' => "\u{26F9}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_BOUNCING_BALL_MEDIUM_SKIN_TONE' => "\u{26F9}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_BOUNCING_BALL_MEDIUM_DARK_SKIN_TONE' => "\u{26F9}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_BOUNCING_BALL_DARK_SKIN_TONE' => "\u{26F9}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_LIFTING_WEIGHTS' => "\u{1F3CB}\u{FE0F}", 'CHARACTER_PERSON_LIFTING_WEIGHTS_LIGHT_SKIN_TONE' => "\u{1F3CB}\u{1F3FB}", 'CHARACTER_PERSON_LIFTING_WEIGHTS_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F3CB}\u{1F3FC}", 'CHARACTER_PERSON_LIFTING_WEIGHTS_MEDIUM_SKIN_TONE' => "\u{1F3CB}\u{1F3FD}", 'CHARACTER_PERSON_LIFTING_WEIGHTS_MEDIUM_DARK_SKIN_TONE' => "\u{1F3CB}\u{1F3FE}", 'CHARACTER_PERSON_LIFTING_WEIGHTS_DARK_SKIN_TONE' => "\u{1F3CB}\u{1F3FF}", 'CHARACTER_MAN_LIFTING_WEIGHTS' => "\u{1F3CB}\u{FE0F}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_LIFTING_WEIGHTS_LIGHT_SKIN_TONE' => "\u{1F3CB}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_LIFTING_WEIGHTS_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F3CB}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_LIFTING_WEIGHTS_MEDIUM_SKIN_TONE' => "\u{1F3CB}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_LIFTING_WEIGHTS_MEDIUM_DARK_SKIN_TONE' => "\u{1F3CB}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_LIFTING_WEIGHTS_DARK_SKIN_TONE' => "\u{1F3CB}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_LIFTING_WEIGHTS' => "\u{1F3CB}\u{FE0F}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_LIFTING_WEIGHTS_LIGHT_SKIN_TONE' => "\u{1F3CB}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_LIFTING_WEIGHTS_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F3CB}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_LIFTING_WEIGHTS_MEDIUM_SKIN_TONE' => "\u{1F3CB}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_LIFTING_WEIGHTS_MEDIUM_DARK_SKIN_TONE' => "\u{1F3CB}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_LIFTING_WEIGHTS_DARK_SKIN_TONE' => "\u{1F3CB}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_BIKING' => "\u{1F6B4}", 'CHARACTER_PERSON_BIKING_LIGHT_SKIN_TONE' => "\u{1F6B4}\u{1F3FB}", 'CHARACTER_PERSON_BIKING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F6B4}\u{1F3FC}", 'CHARACTER_PERSON_BIKING_MEDIUM_SKIN_TONE' => "\u{1F6B4}\u{1F3FD}", 'CHARACTER_PERSON_BIKING_MEDIUM_DARK_SKIN_TONE' => "\u{1F6B4}\u{1F3FE}", 'CHARACTER_PERSON_BIKING_DARK_SKIN_TONE' => "\u{1F6B4}\u{1F3FF}", 'CHARACTER_MAN_BIKING' => "\u{1F6B4}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_BIKING_LIGHT_SKIN_TONE' => "\u{1F6B4}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_BIKING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F6B4}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_BIKING_MEDIUM_SKIN_TONE' => "\u{1F6B4}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_BIKING_MEDIUM_DARK_SKIN_TONE' => "\u{1F6B4}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_BIKING_DARK_SKIN_TONE' => "\u{1F6B4}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_BIKING' => "\u{1F6B4}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_BIKING_LIGHT_SKIN_TONE' => "\u{1F6B4}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_BIKING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F6B4}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_BIKING_MEDIUM_SKIN_TONE' => "\u{1F6B4}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_BIKING_MEDIUM_DARK_SKIN_TONE' => "\u{1F6B4}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_BIKING_DARK_SKIN_TONE' => "\u{1F6B4}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_MOUNTAIN_BIKING' => "\u{1F6B5}", 'CHARACTER_PERSON_MOUNTAIN_BIKING_LIGHT_SKIN_TONE' => "\u{1F6B5}\u{1F3FB}", 'CHARACTER_PERSON_MOUNTAIN_BIKING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F6B5}\u{1F3FC}", 'CHARACTER_PERSON_MOUNTAIN_BIKING_MEDIUM_SKIN_TONE' => "\u{1F6B5}\u{1F3FD}", 'CHARACTER_PERSON_MOUNTAIN_BIKING_MEDIUM_DARK_SKIN_TONE' => "\u{1F6B5}\u{1F3FE}", 'CHARACTER_PERSON_MOUNTAIN_BIKING_DARK_SKIN_TONE' => "\u{1F6B5}\u{1F3FF}", 'CHARACTER_MAN_MOUNTAIN_BIKING' => "\u{1F6B5}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_MOUNTAIN_BIKING_LIGHT_SKIN_TONE' => "\u{1F6B5}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_MOUNTAIN_BIKING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F6B5}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_MOUNTAIN_BIKING_MEDIUM_SKIN_TONE' => "\u{1F6B5}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_MOUNTAIN_BIKING_MEDIUM_DARK_SKIN_TONE' => "\u{1F6B5}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_MOUNTAIN_BIKING_DARK_SKIN_TONE' => "\u{1F6B5}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_MOUNTAIN_BIKING' => "\u{1F6B5}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_MOUNTAIN_BIKING_LIGHT_SKIN_TONE' => "\u{1F6B5}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_MOUNTAIN_BIKING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F6B5}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_MOUNTAIN_BIKING_MEDIUM_SKIN_TONE' => "\u{1F6B5}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_MOUNTAIN_BIKING_MEDIUM_DARK_SKIN_TONE' => "\u{1F6B5}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_MOUNTAIN_BIKING_DARK_SKIN_TONE' => "\u{1F6B5}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_RACING_CAR' => "\u{1F3CE}\u{FE0F}", 'CHARACTER_MOTORCYCLE' => "\u{1F3CD}\u{FE0F}", 'CHARACTER_PERSON_CARTWHEELING' => "\u{1F938}", 'CHARACTER_PERSON_CARTWHEELING_LIGHT_SKIN_TONE' => "\u{1F938}\u{1F3FB}", 'CHARACTER_PERSON_CARTWHEELING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F938}\u{1F3FC}", 'CHARACTER_PERSON_CARTWHEELING_MEDIUM_SKIN_TONE' => "\u{1F938}\u{1F3FD}", 'CHARACTER_PERSON_CARTWHEELING_MEDIUM_DARK_SKIN_TONE' => "\u{1F938}\u{1F3FE}", 'CHARACTER_PERSON_CARTWHEELING_DARK_SKIN_TONE' => "\u{1F938}\u{1F3FF}", 'CHARACTER_MAN_CARTWHEELING' => "\u{1F938}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_CARTWHEELING_LIGHT_SKIN_TONE' => "\u{1F938}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_CARTWHEELING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F938}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_CARTWHEELING_MEDIUM_SKIN_TONE' => "\u{1F938}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_CARTWHEELING_MEDIUM_DARK_SKIN_TONE' => "\u{1F938}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_CARTWHEELING_DARK_SKIN_TONE' => "\u{1F938}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_CARTWHEELING' => "\u{1F938}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_CARTWHEELING_LIGHT_SKIN_TONE' => "\u{1F938}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_CARTWHEELING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F938}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_CARTWHEELING_MEDIUM_SKIN_TONE' => "\u{1F938}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_CARTWHEELING_MEDIUM_DARK_SKIN_TONE' => "\u{1F938}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_CARTWHEELING_DARK_SKIN_TONE' => "\u{1F938}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PEOPLE_WRESTLING' => "\u{1F93C}", 'CHARACTER_MEN_WRESTLING' => "\u{1F93C}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMEN_WRESTLING' => "\u{1F93C}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_PLAYING_WATER_POLO' => "\u{1F93D}", 'CHARACTER_PERSON_PLAYING_WATER_POLO_LIGHT_SKIN_TONE' => "\u{1F93D}\u{1F3FB}", 'CHARACTER_PERSON_PLAYING_WATER_POLO_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F93D}\u{1F3FC}", 'CHARACTER_PERSON_PLAYING_WATER_POLO_MEDIUM_SKIN_TONE' => "\u{1F93D}\u{1F3FD}", 'CHARACTER_PERSON_PLAYING_WATER_POLO_MEDIUM_DARK_SKIN_TONE' => "\u{1F93D}\u{1F3FE}", 'CHARACTER_PERSON_PLAYING_WATER_POLO_DARK_SKIN_TONE' => "\u{1F93D}\u{1F3FF}", 'CHARACTER_MAN_PLAYING_WATER_POLO' => "\u{1F93D}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_PLAYING_WATER_POLO_LIGHT_SKIN_TONE' => "\u{1F93D}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_PLAYING_WATER_POLO_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F93D}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_PLAYING_WATER_POLO_MEDIUM_SKIN_TONE' => "\u{1F93D}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_PLAYING_WATER_POLO_MEDIUM_DARK_SKIN_TONE' => "\u{1F93D}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_PLAYING_WATER_POLO_DARK_SKIN_TONE' => "\u{1F93D}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_PLAYING_WATER_POLO' => "\u{1F93D}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_PLAYING_WATER_POLO_LIGHT_SKIN_TONE' => "\u{1F93D}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_PLAYING_WATER_POLO_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F93D}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_PLAYING_WATER_POLO_MEDIUM_SKIN_TONE' => "\u{1F93D}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_PLAYING_WATER_POLO_MEDIUM_DARK_SKIN_TONE' => "\u{1F93D}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_PLAYING_WATER_POLO_DARK_SKIN_TONE' => "\u{1F93D}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_PLAYING_HANDBALL' => "\u{1F93E}", 'CHARACTER_PERSON_PLAYING_HANDBALL_LIGHT_SKIN_TONE' => "\u{1F93E}\u{1F3FB}", 'CHARACTER_PERSON_PLAYING_HANDBALL_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F93E}\u{1F3FC}", 'CHARACTER_PERSON_PLAYING_HANDBALL_MEDIUM_SKIN_TONE' => "\u{1F93E}\u{1F3FD}", 'CHARACTER_PERSON_PLAYING_HANDBALL_MEDIUM_DARK_SKIN_TONE' => "\u{1F93E}\u{1F3FE}", 'CHARACTER_PERSON_PLAYING_HANDBALL_DARK_SKIN_TONE' => "\u{1F93E}\u{1F3FF}", 'CHARACTER_MAN_PLAYING_HANDBALL' => "\u{1F93E}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_PLAYING_HANDBALL_LIGHT_SKIN_TONE' => "\u{1F93E}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_PLAYING_HANDBALL_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F93E}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_PLAYING_HANDBALL_MEDIUM_SKIN_TONE' => "\u{1F93E}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_PLAYING_HANDBALL_MEDIUM_DARK_SKIN_TONE' => "\u{1F93E}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_PLAYING_HANDBALL_DARK_SKIN_TONE' => "\u{1F93E}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_PLAYING_HANDBALL' => "\u{1F93E}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_PLAYING_HANDBALL_LIGHT_SKIN_TONE' => "\u{1F93E}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_PLAYING_HANDBALL_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F93E}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_PLAYING_HANDBALL_MEDIUM_SKIN_TONE' => "\u{1F93E}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_PLAYING_HANDBALL_MEDIUM_DARK_SKIN_TONE' => "\u{1F93E}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_PLAYING_HANDBALL_DARK_SKIN_TONE' => "\u{1F93E}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_PERSON_JUGGLING' => "\u{1F939}", 'CHARACTER_PERSON_JUGGLING_LIGHT_SKIN_TONE' => "\u{1F939}\u{1F3FB}", 'CHARACTER_PERSON_JUGGLING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F939}\u{1F3FC}", 'CHARACTER_PERSON_JUGGLING_MEDIUM_SKIN_TONE' => "\u{1F939}\u{1F3FD}", 'CHARACTER_PERSON_JUGGLING_MEDIUM_DARK_SKIN_TONE' => "\u{1F939}\u{1F3FE}", 'CHARACTER_PERSON_JUGGLING_DARK_SKIN_TONE' => "\u{1F939}\u{1F3FF}", 'CHARACTER_MAN_JUGGLING' => "\u{1F939}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_JUGGLING_LIGHT_SKIN_TONE' => "\u{1F939}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_JUGGLING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F939}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_JUGGLING_MEDIUM_SKIN_TONE' => "\u{1F939}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_JUGGLING_MEDIUM_DARK_SKIN_TONE' => "\u{1F939}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_MAN_JUGGLING_DARK_SKIN_TONE' => "\u{1F939}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}", 'CHARACTER_WOMAN_JUGGLING' => "\u{1F939}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_JUGGLING_LIGHT_SKIN_TONE' => "\u{1F939}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_JUGGLING_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F939}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_JUGGLING_MEDIUM_SKIN_TONE' => "\u{1F939}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_JUGGLING_MEDIUM_DARK_SKIN_TONE' => "\u{1F939}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_WOMAN_JUGGLING_DARK_SKIN_TONE' => "\u{1F939}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}", 'CHARACTER_MAN_AND_WOMAN_HOLDING_HANDS' => "\u{1F46B}", 'CHARACTER_TWO_MEN_HOLDING_HANDS' => "\u{1F46C}", 'CHARACTER_TWO_WOMEN_HOLDING_HANDS' => "\u{1F46D}", 'CHARACTER_KISS' => "\u{1F48F}", 'CHARACTER_KISS_WOMAN_MAN' => "\u{1F469}\u{200D}\u{2764}\u{FE0F}\u{200D}\u{1F48B}\u{200D}\u{1F468}", 'CHARACTER_KISS_MAN_MAN' => "\u{1F468}\u{200D}\u{2764}\u{FE0F}\u{200D}\u{1F48B}\u{200D}\u{1F468}", 'CHARACTER_KISS_WOMAN_WOMAN' => "\u{1F469}\u{200D}\u{2764}\u{FE0F}\u{200D}\u{1F48B}\u{200D}\u{1F469}", 'CHARACTER_COUPLE_WITH_HEART' => "\u{1F491}", 'CHARACTER_COUPLE_WITH_HEART_WOMAN_MAN' => "\u{1F469}\u{200D}\u{2764}\u{FE0F}\u{200D}\u{1F468}", 'CHARACTER_COUPLE_WITH_HEART_MAN_MAN' => "\u{1F468}\u{200D}\u{2764}\u{FE0F}\u{200D}\u{1F468}", 'CHARACTER_COUPLE_WITH_HEART_WOMAN_WOMAN' => "\u{1F469}\u{200D}\u{2764}\u{FE0F}\u{200D}\u{1F469}", 'CHARACTER_FAMILY' => "\u{1F46A}", 'CHARACTER_FAMILY_MAN_WOMAN_BOY' => "\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}", 'CHARACTER_FAMILY_MAN_WOMAN_GIRL' => "\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}", 'CHARACTER_FAMILY_MAN_WOMAN_GIRL_BOY' => "\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F466}", 'CHARACTER_FAMILY_MAN_WOMAN_BOY_BOY' => "\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}\u{200D}\u{1F466}", 'CHARACTER_FAMILY_MAN_WOMAN_GIRL_GIRL' => "\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F467}", 'CHARACTER_FAMILY_MAN_MAN_BOY' => "\u{1F468}\u{200D}\u{1F468}\u{200D}\u{1F466}", 'CHARACTER_FAMILY_MAN_MAN_GIRL' => "\u{1F468}\u{200D}\u{1F468}\u{200D}\u{1F467}", 'CHARACTER_FAMILY_MAN_MAN_GIRL_BOY' => "\u{1F468}\u{200D}\u{1F468}\u{200D}\u{1F467}\u{200D}\u{1F466}", 'CHARACTER_FAMILY_MAN_MAN_BOY_BOY' => "\u{1F468}\u{200D}\u{1F468}\u{200D}\u{1F466}\u{200D}\u{1F466}", 'CHARACTER_FAMILY_MAN_MAN_GIRL_GIRL' => "\u{1F468}\u{200D}\u{1F468}\u{200D}\u{1F467}\u{200D}\u{1F467}", 'CHARACTER_FAMILY_WOMAN_WOMAN_BOY' => "\u{1F469}\u{200D}\u{1F469}\u{200D}\u{1F466}", 'CHARACTER_FAMILY_WOMAN_WOMAN_GIRL' => "\u{1F469}\u{200D}\u{1F469}\u{200D}\u{1F467}", 'CHARACTER_FAMILY_WOMAN_WOMAN_GIRL_BOY' => "\u{1F469}\u{200D}\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F466}", 'CHARACTER_FAMILY_WOMAN_WOMAN_BOY_BOY' => "\u{1F469}\u{200D}\u{1F469}\u{200D}\u{1F466}\u{200D}\u{1F466}", 'CHARACTER_FAMILY_WOMAN_WOMAN_GIRL_GIRL' => "\u{1F469}\u{200D}\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F467}", 'CHARACTER_FAMILY_MAN_BOY' => "\u{1F468}\u{200D}\u{1F466}", 'CHARACTER_FAMILY_MAN_BOY_BOY' => "\u{1F468}\u{200D}\u{1F466}\u{200D}\u{1F466}", 'CHARACTER_FAMILY_MAN_GIRL' => "\u{1F468}\u{200D}\u{1F467}", 'CHARACTER_FAMILY_MAN_GIRL_BOY' => "\u{1F468}\u{200D}\u{1F467}\u{200D}\u{1F466}", 'CHARACTER_FAMILY_MAN_GIRL_GIRL' => "\u{1F468}\u{200D}\u{1F467}\u{200D}\u{1F467}", 'CHARACTER_FAMILY_WOMAN_BOY' => "\u{1F469}\u{200D}\u{1F466}", 'CHARACTER_FAMILY_WOMAN_BOY_BOY' => "\u{1F469}\u{200D}\u{1F466}\u{200D}\u{1F466}", 'CHARACTER_FAMILY_WOMAN_GIRL' => "\u{1F469}\u{200D}\u{1F467}", 'CHARACTER_FAMILY_WOMAN_GIRL_BOY' => "\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F466}", 'CHARACTER_FAMILY_WOMAN_GIRL_GIRL' => "\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F467}", 'CHARACTER_SELFIE' => "\u{1F933}", 'CHARACTER_SELFIE_LIGHT_SKIN_TONE' => "\u{1F933}\u{1F3FB}", 'CHARACTER_SELFIE_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F933}\u{1F3FC}", 'CHARACTER_SELFIE_MEDIUM_SKIN_TONE' => "\u{1F933}\u{1F3FD}", 'CHARACTER_SELFIE_MEDIUM_DARK_SKIN_TONE' => "\u{1F933}\u{1F3FE}", 'CHARACTER_SELFIE_DARK_SKIN_TONE' => "\u{1F933}\u{1F3FF}", 'CHARACTER_FLEXED_BICEPS' => "\u{1F4AA}", 'CHARACTER_FLEXED_BICEPS_LIGHT_SKIN_TONE' => "\u{1F4AA}\u{1F3FB}", 'CHARACTER_FLEXED_BICEPS_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F4AA}\u{1F3FC}", 'CHARACTER_FLEXED_BICEPS_MEDIUM_SKIN_TONE' => "\u{1F4AA}\u{1F3FD}", 'CHARACTER_FLEXED_BICEPS_MEDIUM_DARK_SKIN_TONE' => "\u{1F4AA}\u{1F3FE}", 'CHARACTER_FLEXED_BICEPS_DARK_SKIN_TONE' => "\u{1F4AA}\u{1F3FF}", 'CHARACTER_LEG' => "\u{1F9B5}", 'CHARACTER_LEG_LIGHT_SKIN_TONE' => "\u{1F9B5}\u{1F3FB}", 'CHARACTER_LEG_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9B5}\u{1F3FC}", 'CHARACTER_LEG_MEDIUM_SKIN_TONE' => "\u{1F9B5}\u{1F3FD}", 'CHARACTER_LEG_MEDIUM_DARK_SKIN_TONE' => "\u{1F9B5}\u{1F3FE}", 'CHARACTER_LEG_DARK_SKIN_TONE' => "\u{1F9B5}\u{1F3FF}", 'CHARACTER_FOOT' => "\u{1F9B6}", 'CHARACTER_FOOT_LIGHT_SKIN_TONE' => "\u{1F9B6}\u{1F3FB}", 'CHARACTER_FOOT_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F9B6}\u{1F3FC}", 'CHARACTER_FOOT_MEDIUM_SKIN_TONE' => "\u{1F9B6}\u{1F3FD}", 'CHARACTER_FOOT_MEDIUM_DARK_SKIN_TONE' => "\u{1F9B6}\u{1F3FE}", 'CHARACTER_FOOT_DARK_SKIN_TONE' => "\u{1F9B6}\u{1F3FF}", 'CHARACTER_BACKHAND_INDEX_POINTING_LEFT' => "\u{1F448}", 'CHARACTER_BACKHAND_INDEX_POINTING_LEFT_LIGHT_SKIN_TONE' => "\u{1F448}\u{1F3FB}", 'CHARACTER_BACKHAND_INDEX_POINTING_LEFT_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F448}\u{1F3FC}", 'CHARACTER_BACKHAND_INDEX_POINTING_LEFT_MEDIUM_SKIN_TONE' => "\u{1F448}\u{1F3FD}", 'CHARACTER_BACKHAND_INDEX_POINTING_LEFT_MEDIUM_DARK_SKIN_TONE' => "\u{1F448}\u{1F3FE}", 'CHARACTER_BACKHAND_INDEX_POINTING_LEFT_DARK_SKIN_TONE' => "\u{1F448}\u{1F3FF}", 'CHARACTER_BACKHAND_INDEX_POINTING_RIGHT' => "\u{1F449}", 'CHARACTER_BACKHAND_INDEX_POINTING_RIGHT_LIGHT_SKIN_TONE' => "\u{1F449}\u{1F3FB}", 'CHARACTER_BACKHAND_INDEX_POINTING_RIGHT_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F449}\u{1F3FC}", 'CHARACTER_BACKHAND_INDEX_POINTING_RIGHT_MEDIUM_SKIN_TONE' => "\u{1F449}\u{1F3FD}", 'CHARACTER_BACKHAND_INDEX_POINTING_RIGHT_MEDIUM_DARK_SKIN_TONE' => "\u{1F449}\u{1F3FE}", 'CHARACTER_BACKHAND_INDEX_POINTING_RIGHT_DARK_SKIN_TONE' => "\u{1F449}\u{1F3FF}", 'CHARACTER_INDEX_POINTING_UP' => "\u{261D}\u{FE0F}", 'CHARACTER_INDEX_POINTING_UP_LIGHT_SKIN_TONE' => "\u{261D}\u{1F3FB}", 'CHARACTER_INDEX_POINTING_UP_MEDIUM_LIGHT_SKIN_TONE' => "\u{261D}\u{1F3FC}", 'CHARACTER_INDEX_POINTING_UP_MEDIUM_SKIN_TONE' => "\u{261D}\u{1F3FD}", 'CHARACTER_INDEX_POINTING_UP_MEDIUM_DARK_SKIN_TONE' => "\u{261D}\u{1F3FE}", 'CHARACTER_INDEX_POINTING_UP_DARK_SKIN_TONE' => "\u{261D}\u{1F3FF}", 'CHARACTER_BACKHAND_INDEX_POINTING_UP' => "\u{1F446}", 'CHARACTER_BACKHAND_INDEX_POINTING_UP_LIGHT_SKIN_TONE' => "\u{1F446}\u{1F3FB}", 'CHARACTER_BACKHAND_INDEX_POINTING_UP_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F446}\u{1F3FC}", 'CHARACTER_BACKHAND_INDEX_POINTING_UP_MEDIUM_SKIN_TONE' => "\u{1F446}\u{1F3FD}", 'CHARACTER_BACKHAND_INDEX_POINTING_UP_MEDIUM_DARK_SKIN_TONE' => "\u{1F446}\u{1F3FE}", 'CHARACTER_BACKHAND_INDEX_POINTING_UP_DARK_SKIN_TONE' => "\u{1F446}\u{1F3FF}", 'CHARACTER_MIDDLE_FINGER' => "\u{1F595}", 'CHARACTER_MIDDLE_FINGER_LIGHT_SKIN_TONE' => "\u{1F595}\u{1F3FB}", 'CHARACTER_MIDDLE_FINGER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F595}\u{1F3FC}", 'CHARACTER_MIDDLE_FINGER_MEDIUM_SKIN_TONE' => "\u{1F595}\u{1F3FD}", 'CHARACTER_MIDDLE_FINGER_MEDIUM_DARK_SKIN_TONE' => "\u{1F595}\u{1F3FE}", 'CHARACTER_MIDDLE_FINGER_DARK_SKIN_TONE' => "\u{1F595}\u{1F3FF}", 'CHARACTER_BACKHAND_INDEX_POINTING_DOWN' => "\u{1F447}", 'CHARACTER_BACKHAND_INDEX_POINTING_DOWN_LIGHT_SKIN_TONE' => "\u{1F447}\u{1F3FB}", 'CHARACTER_BACKHAND_INDEX_POINTING_DOWN_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F447}\u{1F3FC}", 'CHARACTER_BACKHAND_INDEX_POINTING_DOWN_MEDIUM_SKIN_TONE' => "\u{1F447}\u{1F3FD}", 'CHARACTER_BACKHAND_INDEX_POINTING_DOWN_MEDIUM_DARK_SKIN_TONE' => "\u{1F447}\u{1F3FE}", 'CHARACTER_BACKHAND_INDEX_POINTING_DOWN_DARK_SKIN_TONE' => "\u{1F447}\u{1F3FF}", 'CHARACTER_VICTORY_HAND' => "\u{270C}\u{FE0F}", 'CHARACTER_VICTORY_HAND_LIGHT_SKIN_TONE' => "\u{270C}\u{1F3FB}", 'CHARACTER_VICTORY_HAND_MEDIUM_LIGHT_SKIN_TONE' => "\u{270C}\u{1F3FC}", 'CHARACTER_VICTORY_HAND_MEDIUM_SKIN_TONE' => "\u{270C}\u{1F3FD}", 'CHARACTER_VICTORY_HAND_MEDIUM_DARK_SKIN_TONE' => "\u{270C}\u{1F3FE}", 'CHARACTER_VICTORY_HAND_DARK_SKIN_TONE' => "\u{270C}\u{1F3FF}", 'CHARACTER_CROSSED_FINGERS' => "\u{1F91E}", 'CHARACTER_CROSSED_FINGERS_LIGHT_SKIN_TONE' => "\u{1F91E}\u{1F3FB}", 'CHARACTER_CROSSED_FINGERS_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F91E}\u{1F3FC}", 'CHARACTER_CROSSED_FINGERS_MEDIUM_SKIN_TONE' => "\u{1F91E}\u{1F3FD}", 'CHARACTER_CROSSED_FINGERS_MEDIUM_DARK_SKIN_TONE' => "\u{1F91E}\u{1F3FE}", 'CHARACTER_CROSSED_FINGERS_DARK_SKIN_TONE' => "\u{1F91E}\u{1F3FF}", 'CHARACTER_VULCAN_SALUTE' => "\u{1F596}", 'CHARACTER_VULCAN_SALUTE_LIGHT_SKIN_TONE' => "\u{1F596}\u{1F3FB}", 'CHARACTER_VULCAN_SALUTE_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F596}\u{1F3FC}", 'CHARACTER_VULCAN_SALUTE_MEDIUM_SKIN_TONE' => "\u{1F596}\u{1F3FD}", 'CHARACTER_VULCAN_SALUTE_MEDIUM_DARK_SKIN_TONE' => "\u{1F596}\u{1F3FE}", 'CHARACTER_VULCAN_SALUTE_DARK_SKIN_TONE' => "\u{1F596}\u{1F3FF}", 'CHARACTER_SIGN_OF_THE_HORNS' => "\u{1F918}", 'CHARACTER_SIGN_OF_THE_HORNS_LIGHT_SKIN_TONE' => "\u{1F918}\u{1F3FB}", 'CHARACTER_SIGN_OF_THE_HORNS_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F918}\u{1F3FC}", 'CHARACTER_SIGN_OF_THE_HORNS_MEDIUM_SKIN_TONE' => "\u{1F918}\u{1F3FD}", 'CHARACTER_SIGN_OF_THE_HORNS_MEDIUM_DARK_SKIN_TONE' => "\u{1F918}\u{1F3FE}", 'CHARACTER_SIGN_OF_THE_HORNS_DARK_SKIN_TONE' => "\u{1F918}\u{1F3FF}", 'CHARACTER_CALL_ME_HAND' => "\u{1F919}", 'CHARACTER_CALL_ME_HAND_LIGHT_SKIN_TONE' => "\u{1F919}\u{1F3FB}", 'CHARACTER_CALL_ME_HAND_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F919}\u{1F3FC}", 'CHARACTER_CALL_ME_HAND_MEDIUM_SKIN_TONE' => "\u{1F919}\u{1F3FD}", 'CHARACTER_CALL_ME_HAND_MEDIUM_DARK_SKIN_TONE' => "\u{1F919}\u{1F3FE}", 'CHARACTER_CALL_ME_HAND_DARK_SKIN_TONE' => "\u{1F919}\u{1F3FF}", 'CHARACTER_HAND_WITH_FINGERS_SPLAYED' => "\u{1F590}\u{FE0F}", 'CHARACTER_HAND_WITH_FINGERS_SPLAYED_LIGHT_SKIN_TONE' => "\u{1F590}\u{1F3FB}", 'CHARACTER_HAND_WITH_FINGERS_SPLAYED_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F590}\u{1F3FC}", 'CHARACTER_HAND_WITH_FINGERS_SPLAYED_MEDIUM_SKIN_TONE' => "\u{1F590}\u{1F3FD}", 'CHARACTER_HAND_WITH_FINGERS_SPLAYED_MEDIUM_DARK_SKIN_TONE' => "\u{1F590}\u{1F3FE}", 'CHARACTER_HAND_WITH_FINGERS_SPLAYED_DARK_SKIN_TONE' => "\u{1F590}\u{1F3FF}", 'CHARACTER_RAISED_HAND' => "\u{270B}", 'CHARACTER_RAISED_HAND_LIGHT_SKIN_TONE' => "\u{270B}\u{1F3FB}", 'CHARACTER_RAISED_HAND_MEDIUM_LIGHT_SKIN_TONE' => "\u{270B}\u{1F3FC}", 'CHARACTER_RAISED_HAND_MEDIUM_SKIN_TONE' => "\u{270B}\u{1F3FD}", 'CHARACTER_RAISED_HAND_MEDIUM_DARK_SKIN_TONE' => "\u{270B}\u{1F3FE}", 'CHARACTER_RAISED_HAND_DARK_SKIN_TONE' => "\u{270B}\u{1F3FF}", 'CHARACTER_OK_HAND' => "\u{1F44C}", 'CHARACTER_OK_HAND_LIGHT_SKIN_TONE' => "\u{1F44C}\u{1F3FB}", 'CHARACTER_OK_HAND_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F44C}\u{1F3FC}", 'CHARACTER_OK_HAND_MEDIUM_SKIN_TONE' => "\u{1F44C}\u{1F3FD}", 'CHARACTER_OK_HAND_MEDIUM_DARK_SKIN_TONE' => "\u{1F44C}\u{1F3FE}", 'CHARACTER_OK_HAND_DARK_SKIN_TONE' => "\u{1F44C}\u{1F3FF}", 'CHARACTER_THUMBS_UP' => "\u{1F44D}", 'CHARACTER_THUMBS_UP_LIGHT_SKIN_TONE' => "\u{1F44D}\u{1F3FB}", 'CHARACTER_THUMBS_UP_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F44D}\u{1F3FC}", 'CHARACTER_THUMBS_UP_MEDIUM_SKIN_TONE' => "\u{1F44D}\u{1F3FD}", 'CHARACTER_THUMBS_UP_MEDIUM_DARK_SKIN_TONE' => "\u{1F44D}\u{1F3FE}", 'CHARACTER_THUMBS_UP_DARK_SKIN_TONE' => "\u{1F44D}\u{1F3FF}", 'CHARACTER_THUMBS_DOWN' => "\u{1F44E}", 'CHARACTER_THUMBS_DOWN_LIGHT_SKIN_TONE' => "\u{1F44E}\u{1F3FB}", 'CHARACTER_THUMBS_DOWN_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F44E}\u{1F3FC}", 'CHARACTER_THUMBS_DOWN_MEDIUM_SKIN_TONE' => "\u{1F44E}\u{1F3FD}", 'CHARACTER_THUMBS_DOWN_MEDIUM_DARK_SKIN_TONE' => "\u{1F44E}\u{1F3FE}", 'CHARACTER_THUMBS_DOWN_DARK_SKIN_TONE' => "\u{1F44E}\u{1F3FF}", 'CHARACTER_RAISED_FIST' => "\u{270A}", 'CHARACTER_RAISED_FIST_LIGHT_SKIN_TONE' => "\u{270A}\u{1F3FB}", 'CHARACTER_RAISED_FIST_MEDIUM_LIGHT_SKIN_TONE' => "\u{270A}\u{1F3FC}", 'CHARACTER_RAISED_FIST_MEDIUM_SKIN_TONE' => "\u{270A}\u{1F3FD}", 'CHARACTER_RAISED_FIST_MEDIUM_DARK_SKIN_TONE' => "\u{270A}\u{1F3FE}", 'CHARACTER_RAISED_FIST_DARK_SKIN_TONE' => "\u{270A}\u{1F3FF}", 'CHARACTER_ONCOMING_FIST' => "\u{1F44A}", 'CHARACTER_ONCOMING_FIST_LIGHT_SKIN_TONE' => "\u{1F44A}\u{1F3FB}", 'CHARACTER_ONCOMING_FIST_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F44A}\u{1F3FC}", 'CHARACTER_ONCOMING_FIST_MEDIUM_SKIN_TONE' => "\u{1F44A}\u{1F3FD}", 'CHARACTER_ONCOMING_FIST_MEDIUM_DARK_SKIN_TONE' => "\u{1F44A}\u{1F3FE}", 'CHARACTER_ONCOMING_FIST_DARK_SKIN_TONE' => "\u{1F44A}\u{1F3FF}", 'CHARACTER_LEFT_FACING_FIST' => "\u{1F91B}", 'CHARACTER_LEFT_FACING_FIST_LIGHT_SKIN_TONE' => "\u{1F91B}\u{1F3FB}", 'CHARACTER_LEFT_FACING_FIST_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F91B}\u{1F3FC}", 'CHARACTER_LEFT_FACING_FIST_MEDIUM_SKIN_TONE' => "\u{1F91B}\u{1F3FD}", 'CHARACTER_LEFT_FACING_FIST_MEDIUM_DARK_SKIN_TONE' => "\u{1F91B}\u{1F3FE}", 'CHARACTER_LEFT_FACING_FIST_DARK_SKIN_TONE' => "\u{1F91B}\u{1F3FF}", 'CHARACTER_RIGHT_FACING_FIST' => "\u{1F91C}", 'CHARACTER_RIGHT_FACING_FIST_LIGHT_SKIN_TONE' => "\u{1F91C}\u{1F3FB}", 'CHARACTER_RIGHT_FACING_FIST_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F91C}\u{1F3FC}", 'CHARACTER_RIGHT_FACING_FIST_MEDIUM_SKIN_TONE' => "\u{1F91C}\u{1F3FD}", 'CHARACTER_RIGHT_FACING_FIST_MEDIUM_DARK_SKIN_TONE' => "\u{1F91C}\u{1F3FE}", 'CHARACTER_RIGHT_FACING_FIST_DARK_SKIN_TONE' => "\u{1F91C}\u{1F3FF}", 'CHARACTER_RAISED_BACK_OF_HAND' => "\u{1F91A}", 'CHARACTER_RAISED_BACK_OF_HAND_LIGHT_SKIN_TONE' => "\u{1F91A}\u{1F3FB}", 'CHARACTER_RAISED_BACK_OF_HAND_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F91A}\u{1F3FC}", 'CHARACTER_RAISED_BACK_OF_HAND_MEDIUM_SKIN_TONE' => "\u{1F91A}\u{1F3FD}", 'CHARACTER_RAISED_BACK_OF_HAND_MEDIUM_DARK_SKIN_TONE' => "\u{1F91A}\u{1F3FE}", 'CHARACTER_RAISED_BACK_OF_HAND_DARK_SKIN_TONE' => "\u{1F91A}\u{1F3FF}", 'CHARACTER_WAVING_HAND' => "\u{1F44B}", 'CHARACTER_WAVING_HAND_LIGHT_SKIN_TONE' => "\u{1F44B}\u{1F3FB}", 'CHARACTER_WAVING_HAND_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F44B}\u{1F3FC}", 'CHARACTER_WAVING_HAND_MEDIUM_SKIN_TONE' => "\u{1F44B}\u{1F3FD}", 'CHARACTER_WAVING_HAND_MEDIUM_DARK_SKIN_TONE' => "\u{1F44B}\u{1F3FE}", 'CHARACTER_WAVING_HAND_DARK_SKIN_TONE' => "\u{1F44B}\u{1F3FF}", 'CHARACTER_LOVE_YOU_GESTURE' => "\u{1F91F}", 'CHARACTER_LOVE_YOU_GESTURE_LIGHT_SKIN_TONE' => "\u{1F91F}\u{1F3FB}", 'CHARACTER_LOVE_YOU_GESTURE_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F91F}\u{1F3FC}", 'CHARACTER_LOVE_YOU_GESTURE_MEDIUM_SKIN_TONE' => "\u{1F91F}\u{1F3FD}", 'CHARACTER_LOVE_YOU_GESTURE_MEDIUM_DARK_SKIN_TONE' => "\u{1F91F}\u{1F3FE}", 'CHARACTER_LOVE_YOU_GESTURE_DARK_SKIN_TONE' => "\u{1F91F}\u{1F3FF}", 'CHARACTER_WRITING_HAND' => "\u{270D}\u{FE0F}", 'CHARACTER_WRITING_HAND_LIGHT_SKIN_TONE' => "\u{270D}\u{1F3FB}", 'CHARACTER_WRITING_HAND_MEDIUM_LIGHT_SKIN_TONE' => "\u{270D}\u{1F3FC}", 'CHARACTER_WRITING_HAND_MEDIUM_SKIN_TONE' => "\u{270D}\u{1F3FD}", 'CHARACTER_WRITING_HAND_MEDIUM_DARK_SKIN_TONE' => "\u{270D}\u{1F3FE}", 'CHARACTER_WRITING_HAND_DARK_SKIN_TONE' => "\u{270D}\u{1F3FF}", 'CHARACTER_CLAPPING_HANDS' => "\u{1F44F}", 'CHARACTER_CLAPPING_HANDS_LIGHT_SKIN_TONE' => "\u{1F44F}\u{1F3FB}", 'CHARACTER_CLAPPING_HANDS_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F44F}\u{1F3FC}", 'CHARACTER_CLAPPING_HANDS_MEDIUM_SKIN_TONE' => "\u{1F44F}\u{1F3FD}", 'CHARACTER_CLAPPING_HANDS_MEDIUM_DARK_SKIN_TONE' => "\u{1F44F}\u{1F3FE}", 'CHARACTER_CLAPPING_HANDS_DARK_SKIN_TONE' => "\u{1F44F}\u{1F3FF}", 'CHARACTER_OPEN_HANDS' => "\u{1F450}", 'CHARACTER_OPEN_HANDS_LIGHT_SKIN_TONE' => "\u{1F450}\u{1F3FB}", 'CHARACTER_OPEN_HANDS_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F450}\u{1F3FC}", 'CHARACTER_OPEN_HANDS_MEDIUM_SKIN_TONE' => "\u{1F450}\u{1F3FD}", 'CHARACTER_OPEN_HANDS_MEDIUM_DARK_SKIN_TONE' => "\u{1F450}\u{1F3FE}", 'CHARACTER_OPEN_HANDS_DARK_SKIN_TONE' => "\u{1F450}\u{1F3FF}", 'CHARACTER_RAISING_HANDS' => "\u{1F64C}", 'CHARACTER_RAISING_HANDS_LIGHT_SKIN_TONE' => "\u{1F64C}\u{1F3FB}", 'CHARACTER_RAISING_HANDS_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F64C}\u{1F3FC}", 'CHARACTER_RAISING_HANDS_MEDIUM_SKIN_TONE' => "\u{1F64C}\u{1F3FD}", 'CHARACTER_RAISING_HANDS_MEDIUM_DARK_SKIN_TONE' => "\u{1F64C}\u{1F3FE}", 'CHARACTER_RAISING_HANDS_DARK_SKIN_TONE' => "\u{1F64C}\u{1F3FF}", 'CHARACTER_PALMS_UP_TOGETHER' => "\u{1F932}", 'CHARACTER_PALMS_UP_TOGETHER_LIGHT_SKIN_TONE' => "\u{1F932}\u{1F3FB}", 'CHARACTER_PALMS_UP_TOGETHER_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F932}\u{1F3FC}", 'CHARACTER_PALMS_UP_TOGETHER_MEDIUM_SKIN_TONE' => "\u{1F932}\u{1F3FD}", 'CHARACTER_PALMS_UP_TOGETHER_MEDIUM_DARK_SKIN_TONE' => "\u{1F932}\u{1F3FE}", 'CHARACTER_PALMS_UP_TOGETHER_DARK_SKIN_TONE' => "\u{1F932}\u{1F3FF}", 'CHARACTER_FOLDED_HANDS' => "\u{1F64F}", 'CHARACTER_FOLDED_HANDS_LIGHT_SKIN_TONE' => "\u{1F64F}\u{1F3FB}", 'CHARACTER_FOLDED_HANDS_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F64F}\u{1F3FC}", 'CHARACTER_FOLDED_HANDS_MEDIUM_SKIN_TONE' => "\u{1F64F}\u{1F3FD}", 'CHARACTER_FOLDED_HANDS_MEDIUM_DARK_SKIN_TONE' => "\u{1F64F}\u{1F3FE}", 'CHARACTER_FOLDED_HANDS_DARK_SKIN_TONE' => "\u{1F64F}\u{1F3FF}", 'CHARACTER_HANDSHAKE' => "\u{1F91D}", 'CHARACTER_NAIL_POLISH' => "\u{1F485}", 'CHARACTER_NAIL_POLISH_LIGHT_SKIN_TONE' => "\u{1F485}\u{1F3FB}", 'CHARACTER_NAIL_POLISH_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F485}\u{1F3FC}", 'CHARACTER_NAIL_POLISH_MEDIUM_SKIN_TONE' => "\u{1F485}\u{1F3FD}", 'CHARACTER_NAIL_POLISH_MEDIUM_DARK_SKIN_TONE' => "\u{1F485}\u{1F3FE}", 'CHARACTER_NAIL_POLISH_DARK_SKIN_TONE' => "\u{1F485}\u{1F3FF}", 'CHARACTER_EAR' => "\u{1F442}", 'CHARACTER_EAR_LIGHT_SKIN_TONE' => "\u{1F442}\u{1F3FB}", 'CHARACTER_EAR_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F442}\u{1F3FC}", 'CHARACTER_EAR_MEDIUM_SKIN_TONE' => "\u{1F442}\u{1F3FD}", 'CHARACTER_EAR_MEDIUM_DARK_SKIN_TONE' => "\u{1F442}\u{1F3FE}", 'CHARACTER_EAR_DARK_SKIN_TONE' => "\u{1F442}\u{1F3FF}", 'CHARACTER_NOSE' => "\u{1F443}", 'CHARACTER_NOSE_LIGHT_SKIN_TONE' => "\u{1F443}\u{1F3FB}", 'CHARACTER_NOSE_MEDIUM_LIGHT_SKIN_TONE' => "\u{1F443}\u{1F3FC}", 'CHARACTER_NOSE_MEDIUM_SKIN_TONE' => "\u{1F443}\u{1F3FD}", 'CHARACTER_NOSE_MEDIUM_DARK_SKIN_TONE' => "\u{1F443}\u{1F3FE}", 'CHARACTER_NOSE_DARK_SKIN_TONE' => "\u{1F443}\u{1F3FF}", 'CHARACTER_RED_HAIRED' => "\u{1F9B0}", 'CHARACTER_CURLY_HAIRED' => "\u{1F9B1}", 'CHARACTER_BALD' => "\u{1F9B2}", 'CHARACTER_WHITE_HAIRED' => "\u{1F9B3}", 'CHARACTER_FOOTPRINTS' => "\u{1F463}", 'CHARACTER_EYES' => "\u{1F440}", 'CHARACTER_EYE' => "\u{1F441}\u{FE0F}", 'CHARACTER_EYE_IN_SPEECH_BUBBLE' => "\u{1F441}\u{FE0F}\u{200D}\u{1F5E8}\u{FE0F}", 'CHARACTER_BRAIN' => "\u{1F9E0}", 'CHARACTER_BONE' => "\u{1F9B4}", 'CHARACTER_TOOTH' => "\u{1F9B7}", 'CHARACTER_TONGUE' => "\u{1F445}", 'CHARACTER_MOUTH' => "\u{1F444}", 'CHARACTER_KISS_MARK' => "\u{1F48B}", 'CHARACTER_HEART_WITH_ARROW' => "\u{1F498}", 'CHARACTER_RED_HEART' => "\u{2764}\u{FE0F}", 'CHARACTER_BEATING_HEART' => "\u{1F493}", 'CHARACTER_BROKEN_HEART' => "\u{1F494}", 'CHARACTER_TWO_HEARTS' => "\u{1F495}", 'CHARACTER_SPARKLING_HEART' => "\u{1F496}", 'CHARACTER_GROWING_HEART' => "\u{1F497}", 'CHARACTER_BLUE_HEART' => "\u{1F499}", 'CHARACTER_GREEN_HEART' => "\u{1F49A}", 'CHARACTER_YELLOW_HEART' => "\u{1F49B}", 'CHARACTER_ORANGE_HEART' => "\u{1F9E1}", 'CHARACTER_PURPLE_HEART' => "\u{1F49C}", 'CHARACTER_BLACK_HEART' => "\u{1F5A4}", 'CHARACTER_HEART_WITH_RIBBON' => "\u{1F49D}", 'CHARACTER_REVOLVING_HEARTS' => "\u{1F49E}", 'CHARACTER_HEART_DECORATION' => "\u{1F49F}", 'CHARACTER_HEAVY_HEART_EXCLAMATION' => "\u{2763}\u{FE0F}", 'CHARACTER_LOVE_LETTER' => "\u{1F48C}", 'CHARACTER_ZZZ' => "\u{1F4A4}", 'CHARACTER_ANGER_SYMBOL' => "\u{1F4A2}", 'CHARACTER_BOMB' => "\u{1F4A3}", 'CHARACTER_COLLISION' => "\u{1F4A5}", 'CHARACTER_SWEAT_DROPLETS' => "\u{1F4A6}", 'CHARACTER_DASHING_AWAY' => "\u{1F4A8}", 'CHARACTER_DIZZY' => "\u{1F4AB}", 'CHARACTER_SPEECH_BALLOON' => "\u{1F4AC}", 'CHARACTER_LEFT_SPEECH_BUBBLE' => "\u{1F5E8}\u{FE0F}", 'CHARACTER_RIGHT_ANGER_BUBBLE' => "\u{1F5EF}\u{FE0F}", 'CHARACTER_THOUGHT_BALLOON' => "\u{1F4AD}", 'CHARACTER_HOLE' => "\u{1F573}\u{FE0F}", 'CHARACTER_GLASSES' => "\u{1F453}", 'CHARACTER_SUNGLASSES' => "\u{1F576}\u{FE0F}", 'CHARACTER_GOGGLES' => "\u{1F97D}", 'CHARACTER_LAB_COAT' => "\u{1F97C}", 'CHARACTER_NECKTIE' => "\u{1F454}", 'CHARACTER_T_SHIRT' => "\u{1F455}", 'CHARACTER_JEANS' => "\u{1F456}", 'CHARACTER_SCARF' => "\u{1F9E3}", 'CHARACTER_GLOVES' => "\u{1F9E4}", 'CHARACTER_COAT' => "\u{1F9E5}", 'CHARACTER_SOCKS' => "\u{1F9E6}", 'CHARACTER_DRESS' => "\u{1F457}", 'CHARACTER_KIMONO' => "\u{1F458}", 'CHARACTER_BIKINI' => "\u{1F459}", 'CHARACTER_WOMAN_S_CLOTHES' => "\u{1F45A}", 'CHARACTER_PURSE' => "\u{1F45B}", 'CHARACTER_HANDBAG' => "\u{1F45C}", 'CHARACTER_CLUTCH_BAG' => "\u{1F45D}", 'CHARACTER_SHOPPING_BAGS' => "\u{1F6CD}\u{FE0F}", 'CHARACTER_SCHOOL_BACKPACK' => "\u{1F392}", 'CHARACTER_MAN_S_SHOE' => "\u{1F45E}", 'CHARACTER_RUNNING_SHOE' => "\u{1F45F}", 'CHARACTER_HIKING_BOOT' => "\u{1F97E}", 'CHARACTER_WOMAN_S_FLAT_SHOE' => "\u{1F97F}", 'CHARACTER_HIGH_HEELED_SHOE' => "\u{1F460}", 'CHARACTER_WOMAN_S_SANDAL' => "\u{1F461}", 'CHARACTER_WOMAN_S_BOOT' => "\u{1F462}", 'CHARACTER_CROWN' => "\u{1F451}", 'CHARACTER_WOMAN_S_HAT' => "\u{1F452}", 'CHARACTER_TOP_HAT' => "\u{1F3A9}", 'CHARACTER_GRADUATION_CAP' => "\u{1F393}", 'CHARACTER_BILLED_CAP' => "\u{1F9E2}", 'CHARACTER_RESCUE_WORKER_S_HELMET' => "\u{26D1}\u{FE0F}", 'CHARACTER_PRAYER_BEADS' => "\u{1F4FF}", 'CHARACTER_LIPSTICK' => "\u{1F484}", 'CHARACTER_RING' => "\u{1F48D}", 'CHARACTER_GEM_STONE' => "\u{1F48E}", 'CHARACTER_MONKEY_FACE' => "\u{1F435}", 'CHARACTER_MONKEY' => "\u{1F412}", 'CHARACTER_GORILLA' => "\u{1F98D}", 'CHARACTER_DOG_FACE' => "\u{1F436}", 'CHARACTER_DOG' => "\u{1F415}", 'CHARACTER_POODLE' => "\u{1F429}", 'CHARACTER_WOLF_FACE' => "\u{1F43A}", 'CHARACTER_FOX_FACE' => "\u{1F98A}", 'CHARACTER_RACCOON' => "\u{1F99D}", 'CHARACTER_CAT_FACE' => "\u{1F431}", 'CHARACTER_CAT' => "\u{1F408}", 'CHARACTER_LION_FACE' => "\u{1F981}", 'CHARACTER_TIGER_FACE' => "\u{1F42F}", 'CHARACTER_TIGER' => "\u{1F405}", 'CHARACTER_LEOPARD' => "\u{1F406}", 'CHARACTER_HORSE_FACE' => "\u{1F434}", 'CHARACTER_HORSE' => "\u{1F40E}", 'CHARACTER_UNICORN_FACE' => "\u{1F984}", 'CHARACTER_ZEBRA' => "\u{1F993}", 'CHARACTER_DEER' => "\u{1F98C}", 'CHARACTER_COW_FACE' => "\u{1F42E}", 'CHARACTER_OX' => "\u{1F402}", 'CHARACTER_WATER_BUFFALO' => "\u{1F403}", 'CHARACTER_COW' => "\u{1F404}", 'CHARACTER_PIG_FACE' => "\u{1F437}", 'CHARACTER_PIG' => "\u{1F416}", 'CHARACTER_BOAR' => "\u{1F417}", 'CHARACTER_PIG_NOSE' => "\u{1F43D}", 'CHARACTER_RAM' => "\u{1F40F}", 'CHARACTER_EWE' => "\u{1F411}", 'CHARACTER_GOAT' => "\u{1F410}", 'CHARACTER_CAMEL' => "\u{1F42A}", 'CHARACTER_TWO_HUMP_CAMEL' => "\u{1F42B}", 'CHARACTER_LLAMA' => "\u{1F999}", 'CHARACTER_GIRAFFE' => "\u{1F992}", 'CHARACTER_ELEPHANT' => "\u{1F418}", 'CHARACTER_RHINOCEROS' => "\u{1F98F}", 'CHARACTER_HIPPOPOTAMUS' => "\u{1F99B}", 'CHARACTER_MOUSE_FACE' => "\u{1F42D}", 'CHARACTER_MOUSE' => "\u{1F401}", 'CHARACTER_RAT' => "\u{1F400}", 'CHARACTER_HAMSTER_FACE' => "\u{1F439}", 'CHARACTER_RABBIT_FACE' => "\u{1F430}", 'CHARACTER_RABBIT' => "\u{1F407}", 'CHARACTER_CHIPMUNK' => "\u{1F43F}\u{FE0F}", 'CHARACTER_HEDGEHOG' => "\u{1F994}", 'CHARACTER_BAT' => "\u{1F987}", 'CHARACTER_BEAR_FACE' => "\u{1F43B}", 'CHARACTER_KOALA' => "\u{1F428}", 'CHARACTER_PANDA_FACE' => "\u{1F43C}", 'CHARACTER_KANGAROO' => "\u{1F998}", 'CHARACTER_BADGER' => "\u{1F9A1}", 'CHARACTER_PAW_PRINTS' => "\u{1F43E}", 'CHARACTER_TURKEY' => "\u{1F983}", 'CHARACTER_CHICKEN' => "\u{1F414}", 'CHARACTER_ROOSTER' => "\u{1F413}", 'CHARACTER_HATCHING_CHICK' => "\u{1F423}", 'CHARACTER_BABY_CHICK' => "\u{1F424}", 'CHARACTER_FRONT_FACING_BABY_CHICK' => "\u{1F425}", 'CHARACTER_BIRD' => "\u{1F426}", 'CHARACTER_PENGUIN' => "\u{1F427}", 'CHARACTER_DOVE' => "\u{1F54A}\u{FE0F}", 'CHARACTER_EAGLE' => "\u{1F985}", 'CHARACTER_DUCK' => "\u{1F986}", 'CHARACTER_SWAN' => "\u{1F9A2}", 'CHARACTER_OWL' => "\u{1F989}", 'CHARACTER_PEACOCK' => "\u{1F99A}", 'CHARACTER_PARROT' => "\u{1F99C}", 'CHARACTER_FROG_FACE' => "\u{1F438}", 'CHARACTER_CROCODILE' => "\u{1F40A}", 'CHARACTER_TURTLE' => "\u{1F422}", 'CHARACTER_LIZARD' => "\u{1F98E}", 'CHARACTER_SNAKE' => "\u{1F40D}", 'CHARACTER_DRAGON_FACE' => "\u{1F432}", 'CHARACTER_DRAGON' => "\u{1F409}", 'CHARACTER_SAUROPOD' => "\u{1F995}", 'CHARACTER_T_REX' => "\u{1F996}", 'CHARACTER_SPOUTING_WHALE' => "\u{1F433}", 'CHARACTER_WHALE' => "\u{1F40B}", 'CHARACTER_DOLPHIN' => "\u{1F42C}", 'CHARACTER_FISH' => "\u{1F41F}", 'CHARACTER_TROPICAL_FISH' => "\u{1F420}", 'CHARACTER_BLOWFISH' => "\u{1F421}", 'CHARACTER_SHARK' => "\u{1F988}", 'CHARACTER_OCTOPUS' => "\u{1F419}", 'CHARACTER_SPIRAL_SHELL' => "\u{1F41A}", 'CHARACTER_CRAB' => "\u{1F980}", 'CHARACTER_LOBSTER' => "\u{1F99E}", 'CHARACTER_SHRIMP' => "\u{1F990}", 'CHARACTER_SQUID' => "\u{1F991}", 'CHARACTER_SNAIL' => "\u{1F40C}", 'CHARACTER_BUTTERFLY' => "\u{1F98B}", 'CHARACTER_BUG' => "\u{1F41B}", 'CHARACTER_ANT' => "\u{1F41C}", 'CHARACTER_HONEYBEE' => "\u{1F41D}", 'CHARACTER_LADY_BEETLE' => "\u{1F41E}", 'CHARACTER_CRICKET' => "\u{1F997}", 'CHARACTER_SPIDER' => "\u{1F577}\u{FE0F}", 'CHARACTER_SPIDER_WEB' => "\u{1F578}\u{FE0F}", 'CHARACTER_SCORPION' => "\u{1F982}", 'CHARACTER_MOSQUITO' => "\u{1F99F}", 'CHARACTER_MICROBE' => "\u{1F9A0}", 'CHARACTER_BOUQUET' => "\u{1F490}", 'CHARACTER_CHERRY_BLOSSOM' => "\u{1F338}", 'CHARACTER_WHITE_FLOWER' => "\u{1F4AE}", 'CHARACTER_ROSETTE' => "\u{1F3F5}\u{FE0F}", 'CHARACTER_ROSE' => "\u{1F339}", 'CHARACTER_WILTED_FLOWER' => "\u{1F940}", 'CHARACTER_HIBISCUS' => "\u{1F33A}", 'CHARACTER_SUNFLOWER' => "\u{1F33B}", 'CHARACTER_BLOSSOM' => "\u{1F33C}", 'CHARACTER_TULIP' => "\u{1F337}", 'CHARACTER_SEEDLING' => "\u{1F331}", 'CHARACTER_EVERGREEN_TREE' => "\u{1F332}", 'CHARACTER_DECIDUOUS_TREE' => "\u{1F333}", 'CHARACTER_PALM_TREE' => "\u{1F334}", 'CHARACTER_CACTUS' => "\u{1F335}", 'CHARACTER_SHEAF_OF_RICE' => "\u{1F33E}", 'CHARACTER_HERB' => "\u{1F33F}", 'CHARACTER_SHAMROCK' => "\u{2618}\u{FE0F}", 'CHARACTER_FOUR_LEAF_CLOVER' => "\u{1F340}", 'CHARACTER_MAPLE_LEAF' => "\u{1F341}", 'CHARACTER_FALLEN_LEAF' => "\u{1F342}", 'CHARACTER_LEAF_FLUTTERING_IN_WIND' => "\u{1F343}", 'CHARACTER_GRAPES' => "\u{1F347}", 'CHARACTER_MELON' => "\u{1F348}", 'CHARACTER_WATERMELON' => "\u{1F349}", 'CHARACTER_TANGERINE' => "\u{1F34A}", 'CHARACTER_LEMON' => "\u{1F34B}", 'CHARACTER_BANANA' => "\u{1F34C}", 'CHARACTER_PINEAPPLE' => "\u{1F34D}", 'CHARACTER_MANGO' => "\u{1F96D}", 'CHARACTER_RED_APPLE' => "\u{1F34E}", 'CHARACTER_GREEN_APPLE' => "\u{1F34F}", 'CHARACTER_PEAR' => "\u{1F350}", 'CHARACTER_PEACH' => "\u{1F351}", 'CHARACTER_CHERRIES' => "\u{1F352}", 'CHARACTER_STRAWBERRY' => "\u{1F353}", 'CHARACTER_KIWI_FRUIT' => "\u{1F95D}", 'CHARACTER_TOMATO' => "\u{1F345}", 'CHARACTER_COCONUT' => "\u{1F965}", 'CHARACTER_AVOCADO' => "\u{1F951}", 'CHARACTER_EGGPLANT' => "\u{1F346}", 'CHARACTER_POTATO' => "\u{1F954}", 'CHARACTER_CARROT' => "\u{1F955}", 'CHARACTER_EAR_OF_CORN' => "\u{1F33D}", 'CHARACTER_HOT_PEPPER' => "\u{1F336}\u{FE0F}", 'CHARACTER_CUCUMBER' => "\u{1F952}", 'CHARACTER_LEAFY_GREEN' => "\u{1F96C}", 'CHARACTER_BROCCOLI' => "\u{1F966}", 'CHARACTER_MUSHROOM' => "\u{1F344}", 'CHARACTER_PEANUTS' => "\u{1F95C}", 'CHARACTER_CHESTNUT' => "\u{1F330}", 'CHARACTER_BREAD' => "\u{1F35E}", 'CHARACTER_CROISSANT' => "\u{1F950}", 'CHARACTER_BAGUETTE_BREAD' => "\u{1F956}", 'CHARACTER_PRETZEL' => "\u{1F968}", 'CHARACTER_BAGEL' => "\u{1F96F}", 'CHARACTER_PANCAKES' => "\u{1F95E}", 'CHARACTER_CHEESE_WEDGE' => "\u{1F9C0}", 'CHARACTER_MEAT_ON_BONE' => "\u{1F356}", 'CHARACTER_POULTRY_LEG' => "\u{1F357}", 'CHARACTER_CUT_OF_MEAT' => "\u{1F969}", 'CHARACTER_BACON' => "\u{1F953}", 'CHARACTER_HAMBURGER' => "\u{1F354}", 'CHARACTER_FRENCH_FRIES' => "\u{1F35F}", 'CHARACTER_PIZZA' => "\u{1F355}", 'CHARACTER_HOT_DOG' => "\u{1F32D}", 'CHARACTER_SANDWICH' => "\u{1F96A}", 'CHARACTER_TACO' => "\u{1F32E}", 'CHARACTER_BURRITO' => "\u{1F32F}", 'CHARACTER_STUFFED_FLATBREAD' => "\u{1F959}", 'CHARACTER_EGG' => "\u{1F95A}", 'CHARACTER_COOKING' => "\u{1F373}", 'CHARACTER_SHALLOW_PAN_OF_FOOD' => "\u{1F958}", 'CHARACTER_POT_OF_FOOD' => "\u{1F372}", 'CHARACTER_BOWL_WITH_SPOON' => "\u{1F963}", 'CHARACTER_GREEN_SALAD' => "\u{1F957}", 'CHARACTER_POPCORN' => "\u{1F37F}", 'CHARACTER_SALT' => "\u{1F9C2}", 'CHARACTER_CANNED_FOOD' => "\u{1F96B}", 'CHARACTER_BENTO_BOX' => "\u{1F371}", 'CHARACTER_RICE_CRACKER' => "\u{1F358}", 'CHARACTER_RICE_BALL' => "\u{1F359}", 'CHARACTER_COOKED_RICE' => "\u{1F35A}", 'CHARACTER_CURRY_RICE' => "\u{1F35B}", 'CHARACTER_STEAMING_BOWL' => "\u{1F35C}", 'CHARACTER_SPAGHETTI' => "\u{1F35D}", 'CHARACTER_ROASTED_SWEET_POTATO' => "\u{1F360}", 'CHARACTER_ODEN' => "\u{1F362}", 'CHARACTER_SUSHI' => "\u{1F363}", 'CHARACTER_FRIED_SHRIMP' => "\u{1F364}", 'CHARACTER_FISH_CAKE_WITH_SWIRL' => "\u{1F365}", 'CHARACTER_MOON_CAKE' => "\u{1F96E}", 'CHARACTER_DANGO' => "\u{1F361}", 'CHARACTER_DUMPLING' => "\u{1F95F}", 'CHARACTER_FORTUNE_COOKIE' => "\u{1F960}", 'CHARACTER_TAKEOUT_BOX' => "\u{1F961}", 'CHARACTER_SOFT_ICE_CREAM' => "\u{1F366}", 'CHARACTER_SHAVED_ICE' => "\u{1F367}", 'CHARACTER_ICE_CREAM' => "\u{1F368}", 'CHARACTER_DOUGHNUT' => "\u{1F369}", 'CHARACTER_COOKIE' => "\u{1F36A}", 'CHARACTER_BIRTHDAY_CAKE' => "\u{1F382}", 'CHARACTER_SHORTCAKE' => "\u{1F370}", 'CHARACTER_CUPCAKE' => "\u{1F9C1}", 'CHARACTER_PIE' => "\u{1F967}", 'CHARACTER_CHOCOLATE_BAR' => "\u{1F36B}", 'CHARACTER_CANDY' => "\u{1F36C}", 'CHARACTER_LOLLIPOP' => "\u{1F36D}", 'CHARACTER_CUSTARD' => "\u{1F36E}", 'CHARACTER_HONEY_POT' => "\u{1F36F}", 'CHARACTER_BABY_BOTTLE' => "\u{1F37C}", 'CHARACTER_GLASS_OF_MILK' => "\u{1F95B}", 'CHARACTER_HOT_BEVERAGE' => "\u{2615}", 'CHARACTER_TEACUP_WITHOUT_HANDLE' => "\u{1F375}", 'CHARACTER_SAKE' => "\u{1F376}", 'CHARACTER_BOTTLE_WITH_POPPING_CORK' => "\u{1F37E}", 'CHARACTER_WINE_GLASS' => "\u{1F377}", 'CHARACTER_COCKTAIL_GLASS' => "\u{1F378}", 'CHARACTER_TROPICAL_DRINK' => "\u{1F379}", 'CHARACTER_BEER_MUG' => "\u{1F37A}", 'CHARACTER_CLINKING_BEER_MUGS' => "\u{1F37B}", 'CHARACTER_CLINKING_GLASSES' => "\u{1F942}", 'CHARACTER_TUMBLER_GLASS' => "\u{1F943}", 'CHARACTER_CUP_WITH_STRAW' => "\u{1F964}", 'CHARACTER_CHOPSTICKS' => "\u{1F962}", 'CHARACTER_FORK_AND_KNIFE_WITH_PLATE' => "\u{1F37D}\u{FE0F}", 'CHARACTER_FORK_AND_KNIFE' => "\u{1F374}", 'CHARACTER_SPOON' => "\u{1F944}", 'CHARACTER_KITCHEN_KNIFE' => "\u{1F52A}", 'CHARACTER_AMPHORA' => "\u{1F3FA}", 'CHARACTER_GLOBE_SHOWING_EUROPE_AFRICA' => "\u{1F30D}", 'CHARACTER_GLOBE_SHOWING_AMERICAS' => "\u{1F30E}", 'CHARACTER_GLOBE_SHOWING_ASIA_AUSTRALIA' => "\u{1F30F}", 'CHARACTER_GLOBE_WITH_MERIDIANS' => "\u{1F310}", 'CHARACTER_WORLD_MAP' => "\u{1F5FA}\u{FE0F}", 'CHARACTER_MAP_OF_JAPAN' => "\u{1F5FE}", 'CHARACTER_COMPASS' => "\u{1F9ED}", 'CHARACTER_SNOW_CAPPED_MOUNTAIN' => "\u{1F3D4}\u{FE0F}", 'CHARACTER_MOUNTAIN' => "\u{26F0}\u{FE0F}", 'CHARACTER_VOLCANO' => "\u{1F30B}", 'CHARACTER_MOUNT_FUJI' => "\u{1F5FB}", 'CHARACTER_CAMPING' => "\u{1F3D5}\u{FE0F}", 'CHARACTER_BEACH_WITH_UMBRELLA' => "\u{1F3D6}\u{FE0F}", 'CHARACTER_DESERT' => "\u{1F3DC}\u{FE0F}", 'CHARACTER_DESERT_ISLAND' => "\u{1F3DD}\u{FE0F}", 'CHARACTER_NATIONAL_PARK' => "\u{1F3DE}\u{FE0F}", 'CHARACTER_STADIUM' => "\u{1F3DF}\u{FE0F}", 'CHARACTER_CLASSICAL_BUILDING' => "\u{1F3DB}\u{FE0F}", 'CHARACTER_BUILDING_CONSTRUCTION' => "\u{1F3D7}\u{FE0F}", 'CHARACTER_BRICKS' => "\u{1F9F1}", 'CHARACTER_HOUSES' => "\u{1F3D8}\u{FE0F}", 'CHARACTER_DERELICT_HOUSE' => "\u{1F3DA}\u{FE0F}", 'CHARACTER_HOUSE' => "\u{1F3E0}", 'CHARACTER_HOUSE_WITH_GARDEN' => "\u{1F3E1}", 'CHARACTER_OFFICE_BUILDING' => "\u{1F3E2}", 'CHARACTER_JAPANESE_POST_OFFICE' => "\u{1F3E3}", 'CHARACTER_POST_OFFICE' => "\u{1F3E4}", 'CHARACTER_HOSPITAL' => "\u{1F3E5}", 'CHARACTER_BANK' => "\u{1F3E6}", 'CHARACTER_HOTEL' => "\u{1F3E8}", 'CHARACTER_LOVE_HOTEL' => "\u{1F3E9}", 'CHARACTER_CONVENIENCE_STORE' => "\u{1F3EA}", 'CHARACTER_SCHOOL' => "\u{1F3EB}", 'CHARACTER_DEPARTMENT_STORE' => "\u{1F3EC}", 'CHARACTER_FACTORY' => "\u{1F3ED}", 'CHARACTER_JAPANESE_CASTLE' => "\u{1F3EF}", 'CHARACTER_CASTLE' => "\u{1F3F0}", 'CHARACTER_WEDDING' => "\u{1F492}", 'CHARACTER_TOKYO_TOWER' => "\u{1F5FC}", 'CHARACTER_STATUE_OF_LIBERTY' => "\u{1F5FD}", 'CHARACTER_CHURCH' => "\u{26EA}", 'CHARACTER_MOSQUE' => "\u{1F54C}", 'CHARACTER_SYNAGOGUE' => "\u{1F54D}", 'CHARACTER_SHINTO_SHRINE' => "\u{26E9}\u{FE0F}", 'CHARACTER_KAABA' => "\u{1F54B}", 'CHARACTER_FOUNTAIN' => "\u{26F2}", 'CHARACTER_TENT' => "\u{26FA}", 'CHARACTER_FOGGY' => "\u{1F301}", 'CHARACTER_NIGHT_WITH_STARS' => "\u{1F303}", 'CHARACTER_CITYSCAPE' => "\u{1F3D9}\u{FE0F}", 'CHARACTER_SUNRISE_OVER_MOUNTAINS' => "\u{1F304}", 'CHARACTER_SUNRISE' => "\u{1F305}", 'CHARACTER_CITYSCAPE_AT_DUSK' => "\u{1F306}", 'CHARACTER_SUNSET' => "\u{1F307}", 'CHARACTER_BRIDGE_AT_NIGHT' => "\u{1F309}", 'CHARACTER_HOT_SPRINGS' => "\u{2668}\u{FE0F}", 'CHARACTER_MILKY_WAY' => "\u{1F30C}", 'CHARACTER_CAROUSEL_HORSE' => "\u{1F3A0}", 'CHARACTER_FERRIS_WHEEL' => "\u{1F3A1}", 'CHARACTER_ROLLER_COASTER' => "\u{1F3A2}", 'CHARACTER_BARBER_POLE' => "\u{1F488}", 'CHARACTER_CIRCUS_TENT' => "\u{1F3AA}", 'CHARACTER_LOCOMOTIVE' => "\u{1F682}", 'CHARACTER_RAILWAY_CAR' => "\u{1F683}", 'CHARACTER_HIGH_SPEED_TRAIN' => "\u{1F684}", 'CHARACTER_BULLET_TRAIN' => "\u{1F685}", 'CHARACTER_TRAIN' => "\u{1F686}", 'CHARACTER_METRO' => "\u{1F687}", 'CHARACTER_LIGHT_RAIL' => "\u{1F688}", 'CHARACTER_STATION' => "\u{1F689}", 'CHARACTER_TRAM' => "\u{1F68A}", 'CHARACTER_MONORAIL' => "\u{1F69D}", 'CHARACTER_MOUNTAIN_RAILWAY' => "\u{1F69E}", 'CHARACTER_TRAM_CAR' => "\u{1F68B}", 'CHARACTER_BUS' => "\u{1F68C}", 'CHARACTER_ONCOMING_BUS' => "\u{1F68D}", 'CHARACTER_TROLLEYBUS' => "\u{1F68E}", 'CHARACTER_MINIBUS' => "\u{1F690}", 'CHARACTER_AMBULANCE' => "\u{1F691}", 'CHARACTER_FIRE_ENGINE' => "\u{1F692}", 'CHARACTER_POLICE_CAR' => "\u{1F693}", 'CHARACTER_ONCOMING_POLICE_CAR' => "\u{1F694}", 'CHARACTER_TAXI' => "\u{1F695}", 'CHARACTER_ONCOMING_TAXI' => "\u{1F696}", 'CHARACTER_AUTOMOBILE' => "\u{1F697}", 'CHARACTER_ONCOMING_AUTOMOBILE' => "\u{1F698}", 'CHARACTER_SPORT_UTILITY_VEHICLE' => "\u{1F699}", 'CHARACTER_DELIVERY_TRUCK' => "\u{1F69A}", 'CHARACTER_ARTICULATED_LORRY' => "\u{1F69B}", 'CHARACTER_TRACTOR' => "\u{1F69C}", 'CHARACTER_BICYCLE' => "\u{1F6B2}", 'CHARACTER_KICK_SCOOTER' => "\u{1F6F4}", 'CHARACTER_SKATEBOARD' => "\u{1F6F9}", 'CHARACTER_MOTOR_SCOOTER' => "\u{1F6F5}", 'CHARACTER_BUS_STOP' => "\u{1F68F}", 'CHARACTER_MOTORWAY' => "\u{1F6E3}\u{FE0F}", 'CHARACTER_RAILWAY_TRACK' => "\u{1F6E4}\u{FE0F}", 'CHARACTER_OIL_DRUM' => "\u{1F6E2}\u{FE0F}", 'CHARACTER_FUEL_PUMP' => "\u{26FD}", 'CHARACTER_POLICE_CAR_LIGHT' => "\u{1F6A8}", 'CHARACTER_HORIZONTAL_TRAFFIC_LIGHT' => "\u{1F6A5}", 'CHARACTER_VERTICAL_TRAFFIC_LIGHT' => "\u{1F6A6}", 'CHARACTER_STOP_SIGN' => "\u{1F6D1}", 'CHARACTER_CONSTRUCTION' => "\u{1F6A7}", 'CHARACTER_ANCHOR' => "\u{2693}", 'CHARACTER_SAILBOAT' => "\u{26F5}", 'CHARACTER_CANOE' => "\u{1F6F6}", 'CHARACTER_SPEEDBOAT' => "\u{1F6A4}", 'CHARACTER_PASSENGER_SHIP' => "\u{1F6F3}\u{FE0F}", 'CHARACTER_FERRY' => "\u{26F4}\u{FE0F}", 'CHARACTER_MOTOR_BOAT' => "\u{1F6E5}\u{FE0F}", 'CHARACTER_SHIP' => "\u{1F6A2}", 'CHARACTER_AIRPLANE' => "\u{2708}\u{FE0F}", 'CHARACTER_SMALL_AIRPLANE' => "\u{1F6E9}\u{FE0F}", 'CHARACTER_AIRPLANE_DEPARTURE' => "\u{1F6EB}", 'CHARACTER_AIRPLANE_ARRIVAL' => "\u{1F6EC}", 'CHARACTER_SEAT' => "\u{1F4BA}", 'CHARACTER_HELICOPTER' => "\u{1F681}", 'CHARACTER_SUSPENSION_RAILWAY' => "\u{1F69F}", 'CHARACTER_MOUNTAIN_CABLEWAY' => "\u{1F6A0}", 'CHARACTER_AERIAL_TRAMWAY' => "\u{1F6A1}", 'CHARACTER_SATELLITE' => "\u{1F6F0}\u{FE0F}", 'CHARACTER_ROCKET' => "\u{1F680}", 'CHARACTER_FLYING_SAUCER' => "\u{1F6F8}", 'CHARACTER_BELLHOP_BELL' => "\u{1F6CE}\u{FE0F}", 'CHARACTER_LUGGAGE' => "\u{1F9F3}", 'CHARACTER_HOURGLASS_DONE' => "\u{231B}", 'CHARACTER_HOURGLASS_NOT_DONE' => "\u{23F3}", 'CHARACTER_WATCH' => "\u{231A}", 'CHARACTER_ALARM_CLOCK' => "\u{23F0}", 'CHARACTER_STOPWATCH' => "\u{23F1}\u{FE0F}", 'CHARACTER_TIMER_CLOCK' => "\u{23F2}\u{FE0F}", 'CHARACTER_MANTELPIECE_CLOCK' => "\u{1F570}\u{FE0F}", 'CHARACTER_TWELVE_O_CLOCK' => "\u{1F55B}", 'CHARACTER_TWELVE_THIRTY' => "\u{1F567}", 'CHARACTER_ONE_O_CLOCK' => "\u{1F550}", 'CHARACTER_ONE_THIRTY' => "\u{1F55C}", 'CHARACTER_TWO_O_CLOCK' => "\u{1F551}", 'CHARACTER_TWO_THIRTY' => "\u{1F55D}", 'CHARACTER_THREE_O_CLOCK' => "\u{1F552}", 'CHARACTER_THREE_THIRTY' => "\u{1F55E}", 'CHARACTER_FOUR_O_CLOCK' => "\u{1F553}", 'CHARACTER_FOUR_THIRTY' => "\u{1F55F}", 'CHARACTER_FIVE_O_CLOCK' => "\u{1F554}", 'CHARACTER_FIVE_THIRTY' => "\u{1F560}", 'CHARACTER_SIX_O_CLOCK' => "\u{1F555}", 'CHARACTER_SIX_THIRTY' => "\u{1F561}", 'CHARACTER_SEVEN_O_CLOCK' => "\u{1F556}", 'CHARACTER_SEVEN_THIRTY' => "\u{1F562}", 'CHARACTER_EIGHT_O_CLOCK' => "\u{1F557}", 'CHARACTER_EIGHT_THIRTY' => "\u{1F563}", 'CHARACTER_NINE_O_CLOCK' => "\u{1F558}", 'CHARACTER_NINE_THIRTY' => "\u{1F564}", 'CHARACTER_TEN_O_CLOCK' => "\u{1F559}", 'CHARACTER_TEN_THIRTY' => "\u{1F565}", 'CHARACTER_ELEVEN_O_CLOCK' => "\u{1F55A}", 'CHARACTER_ELEVEN_THIRTY' => "\u{1F566}", 'CHARACTER_NEW_MOON' => "\u{1F311}", 'CHARACTER_WAXING_CRESCENT_MOON' => "\u{1F312}", 'CHARACTER_FIRST_QUARTER_MOON' => "\u{1F313}", 'CHARACTER_WAXING_GIBBOUS_MOON' => "\u{1F314}", 'CHARACTER_FULL_MOON' => "\u{1F315}", 'CHARACTER_WANING_GIBBOUS_MOON' => "\u{1F316}", 'CHARACTER_LAST_QUARTER_MOON' => "\u{1F317}", 'CHARACTER_WANING_CRESCENT_MOON' => "\u{1F318}", 'CHARACTER_CRESCENT_MOON' => "\u{1F319}", 'CHARACTER_NEW_MOON_FACE' => "\u{1F31A}", 'CHARACTER_FIRST_QUARTER_MOON_FACE' => "\u{1F31B}", 'CHARACTER_LAST_QUARTER_MOON_FACE' => "\u{1F31C}", 'CHARACTER_THERMOMETER' => "\u{1F321}\u{FE0F}", 'CHARACTER_SUN' => "\u{2600}\u{FE0F}", 'CHARACTER_FULL_MOON_FACE' => "\u{1F31D}", 'CHARACTER_SUN_WITH_FACE' => "\u{1F31E}", 'CHARACTER_STAR' => "\u{2B50}", 'CHARACTER_GLOWING_STAR' => "\u{1F31F}", 'CHARACTER_SHOOTING_STAR' => "\u{1F320}", 'CHARACTER_CLOUD' => "\u{2601}\u{FE0F}", 'CHARACTER_SUN_BEHIND_CLOUD' => "\u{26C5}", 'CHARACTER_CLOUD_WITH_LIGHTNING_AND_RAIN' => "\u{26C8}\u{FE0F}", 'CHARACTER_SUN_BEHIND_SMALL_CLOUD' => "\u{1F324}\u{FE0F}", 'CHARACTER_SUN_BEHIND_LARGE_CLOUD' => "\u{1F325}\u{FE0F}", 'CHARACTER_SUN_BEHIND_RAIN_CLOUD' => "\u{1F326}\u{FE0F}", 'CHARACTER_CLOUD_WITH_RAIN' => "\u{1F327}\u{FE0F}", 'CHARACTER_CLOUD_WITH_SNOW' => "\u{1F328}\u{FE0F}", 'CHARACTER_CLOUD_WITH_LIGHTNING' => "\u{1F329}\u{FE0F}", 'CHARACTER_TORNADO' => "\u{1F32A}\u{FE0F}", 'CHARACTER_FOG' => "\u{1F32B}\u{FE0F}", 'CHARACTER_WIND_FACE' => "\u{1F32C}\u{FE0F}", 'CHARACTER_CYCLONE' => "\u{1F300}", 'CHARACTER_RAINBOW' => "\u{1F308}", 'CHARACTER_CLOSED_UMBRELLA' => "\u{1F302}", 'CHARACTER_UMBRELLA' => "\u{2602}\u{FE0F}", 'CHARACTER_UMBRELLA_WITH_RAIN_DROPS' => "\u{2614}", 'CHARACTER_UMBRELLA_ON_GROUND' => "\u{26F1}\u{FE0F}", 'CHARACTER_HIGH_VOLTAGE' => "\u{26A1}", 'CHARACTER_SNOWFLAKE' => "\u{2744}\u{FE0F}", 'CHARACTER_SNOWMAN' => "\u{2603}\u{FE0F}", 'CHARACTER_SNOWMAN_WITHOUT_SNOW' => "\u{26C4}", 'CHARACTER_COMET' => "\u{2604}\u{FE0F}", 'CHARACTER_FIRE' => "\u{1F525}", 'CHARACTER_DROPLET' => "\u{1F4A7}", 'CHARACTER_WATER_WAVE' => "\u{1F30A}", 'CHARACTER_JACK_O_LANTERN' => "\u{1F383}", 'CHARACTER_CHRISTMAS_TREE' => "\u{1F384}", 'CHARACTER_FIREWORKS' => "\u{1F386}", 'CHARACTER_SPARKLER' => "\u{1F387}", 'CHARACTER_FIRECRACKER' => "\u{1F9E8}", 'CHARACTER_SPARKLES' => "\u{2728}", 'CHARACTER_BALLOON' => "\u{1F388}", 'CHARACTER_PARTY_POPPER' => "\u{1F389}", 'CHARACTER_CONFETTI_BALL' => "\u{1F38A}", 'CHARACTER_TANABATA_TREE' => "\u{1F38B}", 'CHARACTER_PINE_DECORATION' => "\u{1F38D}", 'CHARACTER_JAPANESE_DOLLS' => "\u{1F38E}", 'CHARACTER_CARP_STREAMER' => "\u{1F38F}", 'CHARACTER_WIND_CHIME' => "\u{1F390}", 'CHARACTER_MOON_VIEWING_CEREMONY' => "\u{1F391}", 'CHARACTER_RED_ENVELOPE' => "\u{1F9E7}", 'CHARACTER_RIBBON' => "\u{1F380}", 'CHARACTER_WRAPPED_GIFT' => "\u{1F381}", 'CHARACTER_REMINDER_RIBBON' => "\u{1F397}\u{FE0F}", 'CHARACTER_ADMISSION_TICKETS' => "\u{1F39F}\u{FE0F}", 'CHARACTER_TICKET' => "\u{1F3AB}", 'CHARACTER_MILITARY_MEDAL' => "\u{1F396}\u{FE0F}", 'CHARACTER_TROPHY' => "\u{1F3C6}", 'CHARACTER_SPORTS_MEDAL' => "\u{1F3C5}", 'CHARACTER_1ST_PLACE_MEDAL' => "\u{1F947}", 'CHARACTER_2ND_PLACE_MEDAL' => "\u{1F948}", 'CHARACTER_3RD_PLACE_MEDAL' => "\u{1F949}", 'CHARACTER_SOCCER_BALL' => "\u{26BD}", 'CHARACTER_BASEBALL' => "\u{26BE}", 'CHARACTER_SOFTBALL' => "\u{1F94E}", 'CHARACTER_BASKETBALL' => "\u{1F3C0}", 'CHARACTER_VOLLEYBALL' => "\u{1F3D0}", 'CHARACTER_AMERICAN_FOOTBALL' => "\u{1F3C8}", 'CHARACTER_RUGBY_FOOTBALL' => "\u{1F3C9}", 'CHARACTER_TENNIS' => "\u{1F3BE}", 'CHARACTER_FLYING_DISC' => "\u{1F94F}", 'CHARACTER_BOWLING' => "\u{1F3B3}", 'CHARACTER_CRICKET_GAME' => "\u{1F3CF}", 'CHARACTER_FIELD_HOCKEY' => "\u{1F3D1}", 'CHARACTER_ICE_HOCKEY' => "\u{1F3D2}", 'CHARACTER_LACROSSE' => "\u{1F94D}", 'CHARACTER_PING_PONG' => "\u{1F3D3}", 'CHARACTER_BADMINTON' => "\u{1F3F8}", 'CHARACTER_BOXING_GLOVE' => "\u{1F94A}", 'CHARACTER_MARTIAL_ARTS_UNIFORM' => "\u{1F94B}", 'CHARACTER_GOAL_NET' => "\u{1F945}", 'CHARACTER_FLAG_IN_HOLE' => "\u{26F3}", 'CHARACTER_ICE_SKATE' => "\u{26F8}\u{FE0F}", 'CHARACTER_FISHING_POLE' => "\u{1F3A3}", 'CHARACTER_RUNNING_SHIRT' => "\u{1F3BD}", 'CHARACTER_SKIS' => "\u{1F3BF}", 'CHARACTER_SLED' => "\u{1F6F7}", 'CHARACTER_CURLING_STONE' => "\u{1F94C}", 'CHARACTER_DIRECT_HIT' => "\u{1F3AF}", 'CHARACTER_POOL_8_BALL' => "\u{1F3B1}", 'CHARACTER_CRYSTAL_BALL' => "\u{1F52E}", 'CHARACTER_NAZAR_AMULET' => "\u{1F9FF}", 'CHARACTER_VIDEO_GAME' => "\u{1F3AE}", 'CHARACTER_JOYSTICK' => "\u{1F579}\u{FE0F}", 'CHARACTER_SLOT_MACHINE' => "\u{1F3B0}", 'CHARACTER_GAME_DIE' => "\u{1F3B2}", 'CHARACTER_JIGSAW' => "\u{1F9E9}", 'CHARACTER_TEDDY_BEAR' => "\u{1F9F8}", 'CHARACTER_SPADE_SUIT' => "\u{2660}\u{FE0F}", 'CHARACTER_HEART_SUIT' => "\u{2665}\u{FE0F}", 'CHARACTER_DIAMOND_SUIT' => "\u{2666}\u{FE0F}", 'CHARACTER_CLUB_SUIT' => "\u{2663}\u{FE0F}", 'CHARACTER_CHESS_PAWN' => "\u{265F}\u{FE0F}", 'CHARACTER_JOKER' => "\u{1F0CF}", 'CHARACTER_MAHJONG_RED_DRAGON' => "\u{1F004}", 'CHARACTER_FLOWER_PLAYING_CARDS' => "\u{1F3B4}", 'CHARACTER_PERFORMING_ARTS' => "\u{1F3AD}", 'CHARACTER_FRAMED_PICTURE' => "\u{1F5BC}\u{FE0F}", 'CHARACTER_ARTIST_PALETTE' => "\u{1F3A8}", 'CHARACTER_THREAD' => "\u{1F9F5}", 'CHARACTER_YARN' => "\u{1F9F6}", 'CHARACTER_MUTED_SPEAKER' => "\u{1F507}", 'CHARACTER_SPEAKER_LOW_VOLUME' => "\u{1F508}", 'CHARACTER_SPEAKER_MEDIUM_VOLUME' => "\u{1F509}", 'CHARACTER_SPEAKER_HIGH_VOLUME' => "\u{1F50A}", 'CHARACTER_LOUDSPEAKER' => "\u{1F4E2}", 'CHARACTER_MEGAPHONE' => "\u{1F4E3}", 'CHARACTER_POSTAL_HORN' => "\u{1F4EF}", 'CHARACTER_BELL' => "\u{1F514}", 'CHARACTER_BELL_WITH_SLASH' => "\u{1F515}", 'CHARACTER_MUSICAL_SCORE' => "\u{1F3BC}", 'CHARACTER_MUSICAL_NOTE' => "\u{1F3B5}", 'CHARACTER_MUSICAL_NOTES' => "\u{1F3B6}", 'CHARACTER_STUDIO_MICROPHONE' => "\u{1F399}\u{FE0F}", 'CHARACTER_LEVEL_SLIDER' => "\u{1F39A}\u{FE0F}", 'CHARACTER_CONTROL_KNOBS' => "\u{1F39B}\u{FE0F}", 'CHARACTER_MICROPHONE' => "\u{1F3A4}", 'CHARACTER_HEADPHONE' => "\u{1F3A7}", 'CHARACTER_RADIO' => "\u{1F4FB}", 'CHARACTER_SAXOPHONE' => "\u{1F3B7}", 'CHARACTER_GUITAR' => "\u{1F3B8}", 'CHARACTER_MUSICAL_KEYBOARD' => "\u{1F3B9}", 'CHARACTER_TRUMPET' => "\u{1F3BA}", 'CHARACTER_VIOLIN' => "\u{1F3BB}", 'CHARACTER_DRUM' => "\u{1F941}", 'CHARACTER_MOBILE_PHONE' => "\u{1F4F1}", 'CHARACTER_MOBILE_PHONE_WITH_ARROW' => "\u{1F4F2}", 'CHARACTER_TELEPHONE' => "\u{260E}\u{FE0F}", 'CHARACTER_TELEPHONE_RECEIVER' => "\u{1F4DE}", 'CHARACTER_PAGER' => "\u{1F4DF}", 'CHARACTER_FAX_MACHINE' => "\u{1F4E0}", 'CHARACTER_BATTERY' => "\u{1F50B}", 'CHARACTER_ELECTRIC_PLUG' => "\u{1F50C}", 'CHARACTER_LAPTOP_COMPUTER' => "\u{1F4BB}", 'CHARACTER_DESKTOP_COMPUTER' => "\u{1F5A5}\u{FE0F}", 'CHARACTER_PRINTER' => "\u{1F5A8}\u{FE0F}", 'CHARACTER_KEYBOARD' => "\u{2328}\u{FE0F}", 'CHARACTER_COMPUTER_MOUSE' => "\u{1F5B1}\u{FE0F}", 'CHARACTER_TRACKBALL' => "\u{1F5B2}\u{FE0F}", 'CHARACTER_COMPUTER_DISK' => "\u{1F4BD}", 'CHARACTER_FLOPPY_DISK' => "\u{1F4BE}", 'CHARACTER_OPTICAL_DISK' => "\u{1F4BF}", 'CHARACTER_DVD' => "\u{1F4C0}", 'CHARACTER_ABACUS' => "\u{1F9EE}", 'CHARACTER_MOVIE_CAMERA' => "\u{1F3A5}", 'CHARACTER_FILM_FRAMES' => "\u{1F39E}\u{FE0F}", 'CHARACTER_FILM_PROJECTOR' => "\u{1F4FD}\u{FE0F}", 'CHARACTER_CLAPPER_BOARD' => "\u{1F3AC}", 'CHARACTER_TELEVISION' => "\u{1F4FA}", 'CHARACTER_CAMERA' => "\u{1F4F7}", 'CHARACTER_CAMERA_WITH_FLASH' => "\u{1F4F8}", 'CHARACTER_VIDEO_CAMERA' => "\u{1F4F9}", 'CHARACTER_VIDEOCASSETTE' => "\u{1F4FC}", 'CHARACTER_MAGNIFYING_GLASS_TILTED_LEFT' => "\u{1F50D}", 'CHARACTER_MAGNIFYING_GLASS_TILTED_RIGHT' => "\u{1F50E}", 'CHARACTER_CANDLE' => "\u{1F56F}\u{FE0F}", 'CHARACTER_LIGHT_BULB' => "\u{1F4A1}", 'CHARACTER_FLASHLIGHT' => "\u{1F526}", 'CHARACTER_RED_PAPER_LANTERN' => "\u{1F3EE}", 'CHARACTER_NOTEBOOK_WITH_DECORATIVE_COVER' => "\u{1F4D4}", 'CHARACTER_CLOSED_BOOK' => "\u{1F4D5}", 'CHARACTER_OPEN_BOOK' => "\u{1F4D6}", 'CHARACTER_GREEN_BOOK' => "\u{1F4D7}", 'CHARACTER_BLUE_BOOK' => "\u{1F4D8}", 'CHARACTER_ORANGE_BOOK' => "\u{1F4D9}", 'CHARACTER_BOOKS' => "\u{1F4DA}", 'CHARACTER_NOTEBOOK' => "\u{1F4D3}", 'CHARACTER_LEDGER' => "\u{1F4D2}", 'CHARACTER_PAGE_WITH_CURL' => "\u{1F4C3}", 'CHARACTER_SCROLL' => "\u{1F4DC}", 'CHARACTER_PAGE_FACING_UP' => "\u{1F4C4}", 'CHARACTER_NEWSPAPER' => "\u{1F4F0}", 'CHARACTER_ROLLED_UP_NEWSPAPER' => "\u{1F5DE}\u{FE0F}", 'CHARACTER_BOOKMARK_TABS' => "\u{1F4D1}", 'CHARACTER_BOOKMARK' => "\u{1F516}", 'CHARACTER_LABEL' => "\u{1F3F7}\u{FE0F}", 'CHARACTER_MONEY_BAG' => "\u{1F4B0}", 'CHARACTER_YEN_BANKNOTE' => "\u{1F4B4}", 'CHARACTER_DOLLAR_BANKNOTE' => "\u{1F4B5}", 'CHARACTER_EURO_BANKNOTE' => "\u{1F4B6}", 'CHARACTER_POUND_BANKNOTE' => "\u{1F4B7}", 'CHARACTER_MONEY_WITH_WINGS' => "\u{1F4B8}", 'CHARACTER_CREDIT_CARD' => "\u{1F4B3}", 'CHARACTER_RECEIPT' => "\u{1F9FE}", 'CHARACTER_CHART_INCREASING_WITH_YEN' => "\u{1F4B9}", 'CHARACTER_CURRENCY_EXCHANGE' => "\u{1F4B1}", 'CHARACTER_HEAVY_DOLLAR_SIGN' => "\u{1F4B2}", 'CHARACTER_ENVELOPE' => "\u{2709}\u{FE0F}", 'CHARACTER_E_MAIL' => "\u{1F4E7}", 'CHARACTER_INCOMING_ENVELOPE' => "\u{1F4E8}", 'CHARACTER_ENVELOPE_WITH_ARROW' => "\u{1F4E9}", 'CHARACTER_OUTBOX_TRAY' => "\u{1F4E4}", 'CHARACTER_INBOX_TRAY' => "\u{1F4E5}", 'CHARACTER_PACKAGE' => "\u{1F4E6}", 'CHARACTER_CLOSED_MAILBOX_WITH_RAISED_FLAG' => "\u{1F4EB}", 'CHARACTER_CLOSED_MAILBOX_WITH_LOWERED_FLAG' => "\u{1F4EA}", 'CHARACTER_OPEN_MAILBOX_WITH_RAISED_FLAG' => "\u{1F4EC}", 'CHARACTER_OPEN_MAILBOX_WITH_LOWERED_FLAG' => "\u{1F4ED}", 'CHARACTER_POSTBOX' => "\u{1F4EE}", 'CHARACTER_BALLOT_BOX_WITH_BALLOT' => "\u{1F5F3}\u{FE0F}", 'CHARACTER_PENCIL' => "\u{270F}\u{FE0F}", 'CHARACTER_BLACK_NIB' => "\u{2712}\u{FE0F}", 'CHARACTER_FOUNTAIN_PEN' => "\u{1F58B}\u{FE0F}", 'CHARACTER_PEN' => "\u{1F58A}\u{FE0F}", 'CHARACTER_PAINTBRUSH' => "\u{1F58C}\u{FE0F}", 'CHARACTER_CRAYON' => "\u{1F58D}\u{FE0F}", 'CHARACTER_MEMO' => "\u{1F4DD}", 'CHARACTER_BRIEFCASE' => "\u{1F4BC}", 'CHARACTER_FILE_FOLDER' => "\u{1F4C1}", 'CHARACTER_OPEN_FILE_FOLDER' => "\u{1F4C2}", 'CHARACTER_CARD_INDEX_DIVIDERS' => "\u{1F5C2}\u{FE0F}", 'CHARACTER_CALENDAR' => "\u{1F4C5}", 'CHARACTER_TEAR_OFF_CALENDAR' => "\u{1F4C6}", 'CHARACTER_SPIRAL_NOTEPAD' => "\u{1F5D2}\u{FE0F}", 'CHARACTER_SPIRAL_CALENDAR' => "\u{1F5D3}\u{FE0F}", 'CHARACTER_CARD_INDEX' => "\u{1F4C7}", 'CHARACTER_CHART_INCREASING' => "\u{1F4C8}", 'CHARACTER_CHART_DECREASING' => "\u{1F4C9}", 'CHARACTER_BAR_CHART' => "\u{1F4CA}", 'CHARACTER_CLIPBOARD' => "\u{1F4CB}", 'CHARACTER_PUSHPIN' => "\u{1F4CC}", 'CHARACTER_ROUND_PUSHPIN' => "\u{1F4CD}", 'CHARACTER_PAPERCLIP' => "\u{1F4CE}", 'CHARACTER_LINKED_PAPERCLIPS' => "\u{1F587}\u{FE0F}", 'CHARACTER_STRAIGHT_RULER' => "\u{1F4CF}", 'CHARACTER_TRIANGULAR_RULER' => "\u{1F4D0}", 'CHARACTER_SCISSORS' => "\u{2702}\u{FE0F}", 'CHARACTER_CARD_FILE_BOX' => "\u{1F5C3}\u{FE0F}", 'CHARACTER_FILE_CABINET' => "\u{1F5C4}\u{FE0F}", 'CHARACTER_WASTEBASKET' => "\u{1F5D1}\u{FE0F}", 'CHARACTER_LOCKED' => "\u{1F512}", 'CHARACTER_UNLOCKED' => "\u{1F513}", 'CHARACTER_LOCKED_WITH_PEN' => "\u{1F50F}", 'CHARACTER_LOCKED_WITH_KEY' => "\u{1F510}", 'CHARACTER_KEY' => "\u{1F511}", 'CHARACTER_OLD_KEY' => "\u{1F5DD}\u{FE0F}", 'CHARACTER_HAMMER' => "\u{1F528}", 'CHARACTER_PICK' => "\u{26CF}\u{FE0F}", 'CHARACTER_HAMMER_AND_PICK' => "\u{2692}\u{FE0F}", 'CHARACTER_HAMMER_AND_WRENCH' => "\u{1F6E0}\u{FE0F}", 'CHARACTER_DAGGER' => "\u{1F5E1}\u{FE0F}", 'CHARACTER_CROSSED_SWORDS' => "\u{2694}\u{FE0F}", 'CHARACTER_PISTOL' => "\u{1F52B}", 'CHARACTER_BOW_AND_ARROW' => "\u{1F3F9}", 'CHARACTER_SHIELD' => "\u{1F6E1}\u{FE0F}", 'CHARACTER_WRENCH' => "\u{1F527}", 'CHARACTER_NUT_AND_BOLT' => "\u{1F529}", 'CHARACTER_GEAR' => "\u{2699}\u{FE0F}", 'CHARACTER_CLAMP' => "\u{1F5DC}\u{FE0F}", 'CHARACTER_BALANCE_SCALE' => "\u{2696}\u{FE0F}", 'CHARACTER_LINK' => "\u{1F517}", 'CHARACTER_CHAINS' => "\u{26D3}\u{FE0F}", 'CHARACTER_TOOLBOX' => "\u{1F9F0}", 'CHARACTER_MAGNET' => "\u{1F9F2}", 'CHARACTER_ALEMBIC' => "\u{2697}\u{FE0F}", 'CHARACTER_TEST_TUBE' => "\u{1F9EA}", 'CHARACTER_PETRI_DISH' => "\u{1F9EB}", 'CHARACTER_DNA' => "\u{1F9EC}", 'CHARACTER_MICROSCOPE' => "\u{1F52C}", 'CHARACTER_TELESCOPE' => "\u{1F52D}", 'CHARACTER_SATELLITE_ANTENNA' => "\u{1F4E1}", 'CHARACTER_SYRINGE' => "\u{1F489}", 'CHARACTER_PILL' => "\u{1F48A}", 'CHARACTER_DOOR' => "\u{1F6AA}", 'CHARACTER_BED' => "\u{1F6CF}\u{FE0F}", 'CHARACTER_COUCH_AND_LAMP' => "\u{1F6CB}\u{FE0F}", 'CHARACTER_TOILET' => "\u{1F6BD}", 'CHARACTER_SHOWER' => "\u{1F6BF}", 'CHARACTER_BATHTUB' => "\u{1F6C1}", 'CHARACTER_LOTION_BOTTLE' => "\u{1F9F4}", 'CHARACTER_SAFETY_PIN' => "\u{1F9F7}", 'CHARACTER_BROOM' => "\u{1F9F9}", 'CHARACTER_BASKET' => "\u{1F9FA}", 'CHARACTER_ROLL_OF_PAPER' => "\u{1F9FB}", 'CHARACTER_SOAP' => "\u{1F9FC}", 'CHARACTER_SPONGE' => "\u{1F9FD}", 'CHARACTER_FIRE_EXTINGUISHER' => "\u{1F9EF}", 'CHARACTER_SHOPPING_CART' => "\u{1F6D2}", 'CHARACTER_CIGARETTE' => "\u{1F6AC}", 'CHARACTER_COFFIN' => "\u{26B0}\u{FE0F}", 'CHARACTER_FUNERAL_URN' => "\u{26B1}\u{FE0F}", 'CHARACTER_MOAI' => "\u{1F5FF}", 'CHARACTER_ATM_SIGN' => "\u{1F3E7}", 'CHARACTER_LITTER_IN_BIN_SIGN' => "\u{1F6AE}", 'CHARACTER_POTABLE_WATER' => "\u{1F6B0}", 'CHARACTER_WHEELCHAIR_SYMBOL' => "\u{267F}", 'CHARACTER_MEN_S_ROOM' => "\u{1F6B9}", 'CHARACTER_WOMEN_S_ROOM' => "\u{1F6BA}", 'CHARACTER_RESTROOM' => "\u{1F6BB}", 'CHARACTER_BABY_SYMBOL' => "\u{1F6BC}", 'CHARACTER_WATER_CLOSET' => "\u{1F6BE}", 'CHARACTER_PASSPORT_CONTROL' => "\u{1F6C2}", 'CHARACTER_CUSTOMS' => "\u{1F6C3}", 'CHARACTER_BAGGAGE_CLAIM' => "\u{1F6C4}", 'CHARACTER_LEFT_LUGGAGE' => "\u{1F6C5}", 'CHARACTER_WARNING' => "\u{26A0}\u{FE0F}", 'CHARACTER_CHILDREN_CROSSING' => "\u{1F6B8}", 'CHARACTER_NO_ENTRY' => "\u{26D4}", 'CHARACTER_PROHIBITED' => "\u{1F6AB}", 'CHARACTER_NO_BICYCLES' => "\u{1F6B3}", 'CHARACTER_NO_SMOKING' => "\u{1F6AD}", 'CHARACTER_NO_LITTERING' => "\u{1F6AF}", 'CHARACTER_NON_POTABLE_WATER' => "\u{1F6B1}", 'CHARACTER_NO_PEDESTRIANS' => "\u{1F6B7}", 'CHARACTER_NO_MOBILE_PHONES' => "\u{1F4F5}", 'CHARACTER_NO_ONE_UNDER_EIGHTEEN' => "\u{1F51E}", 'CHARACTER_RADIOACTIVE' => "\u{2622}\u{FE0F}", 'CHARACTER_BIOHAZARD' => "\u{2623}\u{FE0F}", 'CHARACTER_UP_ARROW' => "\u{2B06}\u{FE0F}", 'CHARACTER_UP_RIGHT_ARROW' => "\u{2197}\u{FE0F}", 'CHARACTER_RIGHT_ARROW' => "\u{27A1}\u{FE0F}", 'CHARACTER_DOWN_RIGHT_ARROW' => "\u{2198}\u{FE0F}", 'CHARACTER_DOWN_ARROW' => "\u{2B07}\u{FE0F}", 'CHARACTER_DOWN_LEFT_ARROW' => "\u{2199}\u{FE0F}", 'CHARACTER_LEFT_ARROW' => "\u{2B05}\u{FE0F}", 'CHARACTER_UP_LEFT_ARROW' => "\u{2196}\u{FE0F}", 'CHARACTER_UP_DOWN_ARROW' => "\u{2195}\u{FE0F}", 'CHARACTER_LEFT_RIGHT_ARROW' => "\u{2194}\u{FE0F}", 'CHARACTER_RIGHT_ARROW_CURVING_LEFT' => "\u{21A9}\u{FE0F}", 'CHARACTER_LEFT_ARROW_CURVING_RIGHT' => "\u{21AA}\u{FE0F}", 'CHARACTER_RIGHT_ARROW_CURVING_UP' => "\u{2934}\u{FE0F}", 'CHARACTER_RIGHT_ARROW_CURVING_DOWN' => "\u{2935}\u{FE0F}", 'CHARACTER_CLOCKWISE_VERTICAL_ARROWS' => "\u{1F503}", 'CHARACTER_COUNTERCLOCKWISE_ARROWS_BUTTON' => "\u{1F504}", 'CHARACTER_BACK_ARROW' => "\u{1F519}", 'CHARACTER_END_ARROW' => "\u{1F51A}", 'CHARACTER_ON_ARROW' => "\u{1F51B}", 'CHARACTER_SOON_ARROW' => "\u{1F51C}", 'CHARACTER_TOP_ARROW' => "\u{1F51D}", 'CHARACTER_PLACE_OF_WORSHIP' => "\u{1F6D0}", 'CHARACTER_ATOM_SYMBOL' => "\u{269B}\u{FE0F}", 'CHARACTER_OM' => "\u{1F549}\u{FE0F}", 'CHARACTER_STAR_OF_DAVID' => "\u{2721}\u{FE0F}", 'CHARACTER_WHEEL_OF_DHARMA' => "\u{2638}\u{FE0F}", 'CHARACTER_YIN_YANG' => "\u{262F}\u{FE0F}", 'CHARACTER_LATIN_CROSS' => "\u{271D}\u{FE0F}", 'CHARACTER_ORTHODOX_CROSS' => "\u{2626}\u{FE0F}", 'CHARACTER_STAR_AND_CRESCENT' => "\u{262A}\u{FE0F}", 'CHARACTER_PEACE_SYMBOL' => "\u{262E}\u{FE0F}", 'CHARACTER_MENORAH' => "\u{1F54E}", 'CHARACTER_DOTTED_SIX_POINTED_STAR' => "\u{1F52F}", 'CHARACTER_ARIES' => "\u{2648}", 'CHARACTER_TAURUS' => "\u{2649}", 'CHARACTER_GEMINI' => "\u{264A}", 'CHARACTER_CANCER' => "\u{264B}", 'CHARACTER_LEO' => "\u{264C}", 'CHARACTER_VIRGO' => "\u{264D}", 'CHARACTER_LIBRA' => "\u{264E}", 'CHARACTER_SCORPIO' => "\u{264F}", 'CHARACTER_SAGITTARIUS' => "\u{2650}", 'CHARACTER_CAPRICORN' => "\u{2651}", 'CHARACTER_AQUARIUS' => "\u{2652}", 'CHARACTER_PISCES' => "\u{2653}", 'CHARACTER_OPHIUCHUS' => "\u{26CE}", 'CHARACTER_SHUFFLE_TRACKS_BUTTON' => "\u{1F500}", 'CHARACTER_REPEAT_BUTTON' => "\u{1F501}", 'CHARACTER_REPEAT_SINGLE_BUTTON' => "\u{1F502}", 'CHARACTER_PLAY_BUTTON' => "\u{25B6}\u{FE0F}", 'CHARACTER_FAST_FORWARD_BUTTON' => "\u{23E9}", 'CHARACTER_NEXT_TRACK_BUTTON' => "\u{23ED}\u{FE0F}", 'CHARACTER_PLAY_OR_PAUSE_BUTTON' => "\u{23EF}\u{FE0F}", 'CHARACTER_REVERSE_BUTTON' => "\u{25C0}\u{FE0F}", 'CHARACTER_FAST_REVERSE_BUTTON' => "\u{23EA}", 'CHARACTER_LAST_TRACK_BUTTON' => "\u{23EE}\u{FE0F}", 'CHARACTER_UPWARDS_BUTTON' => "\u{1F53C}", 'CHARACTER_FAST_UP_BUTTON' => "\u{23EB}", 'CHARACTER_DOWNWARDS_BUTTON' => "\u{1F53D}", 'CHARACTER_FAST_DOWN_BUTTON' => "\u{23EC}", 'CHARACTER_PAUSE_BUTTON' => "\u{23F8}\u{FE0F}", 'CHARACTER_STOP_BUTTON' => "\u{23F9}\u{FE0F}", 'CHARACTER_RECORD_BUTTON' => "\u{23FA}\u{FE0F}", 'CHARACTER_EJECT_BUTTON' => "\u{23CF}\u{FE0F}", 'CHARACTER_CINEMA' => "\u{1F3A6}", 'CHARACTER_DIM_BUTTON' => "\u{1F505}", 'CHARACTER_BRIGHT_BUTTON' => "\u{1F506}", 'CHARACTER_ANTENNA_BARS' => "\u{1F4F6}", 'CHARACTER_VIBRATION_MODE' => "\u{1F4F3}", 'CHARACTER_MOBILE_PHONE_OFF' => "\u{1F4F4}", 'CHARACTER_FEMALE_SIGN' => "\u{2640}\u{FE0F}", 'CHARACTER_MALE_SIGN' => "\u{2642}\u{FE0F}", 'CHARACTER_MEDICAL_SYMBOL' => "\u{2695}\u{FE0F}", 'CHARACTER_INFINITY' => "\u{267E}\u{FE0F}", 'CHARACTER_RECYCLING_SYMBOL' => "\u{267B}\u{FE0F}", 'CHARACTER_FLEUR_DE_LIS' => "\u{269C}\u{FE0F}", 'CHARACTER_TRIDENT_EMBLEM' => "\u{1F531}", 'CHARACTER_NAME_BADGE' => "\u{1F4DB}", 'CHARACTER_JAPANESE_SYMBOL_FOR_BEGINNER' => "\u{1F530}", 'CHARACTER_HEAVY_LARGE_CIRCLE' => "\u{2B55}", 'CHARACTER_WHITE_HEAVY_CHECK_MARK' => "\u{2705}", 'CHARACTER_BALLOT_BOX_WITH_CHECK' => "\u{2611}\u{FE0F}", 'CHARACTER_HEAVY_CHECK_MARK' => "\u{2714}\u{FE0F}", 'CHARACTER_HEAVY_MULTIPLICATION_X' => "\u{2716}\u{FE0F}", 'CHARACTER_CROSS_MARK' => "\u{274C}", 'CHARACTER_CROSS_MARK_BUTTON' => "\u{274E}", 'CHARACTER_HEAVY_PLUS_SIGN' => "\u{2795}", 'CHARACTER_HEAVY_MINUS_SIGN' => "\u{2796}", 'CHARACTER_HEAVY_DIVISION_SIGN' => "\u{2797}", 'CHARACTER_CURLY_LOOP' => "\u{27B0}", 'CHARACTER_DOUBLE_CURLY_LOOP' => "\u{27BF}", 'CHARACTER_PART_ALTERNATION_MARK' => "\u{303D}\u{FE0F}", 'CHARACTER_EIGHT_SPOKED_ASTERISK' => "\u{2733}\u{FE0F}", 'CHARACTER_EIGHT_POINTED_STAR' => "\u{2734}\u{FE0F}", 'CHARACTER_SPARKLE' => "\u{2747}\u{FE0F}", 'CHARACTER_DOUBLE_EXCLAMATION_MARK' => "\u{203C}\u{FE0F}", 'CHARACTER_EXCLAMATION_QUESTION_MARK' => "\u{2049}\u{FE0F}", 'CHARACTER_QUESTION_MARK' => "\u{2753}", 'CHARACTER_WHITE_QUESTION_MARK' => "\u{2754}", 'CHARACTER_WHITE_EXCLAMATION_MARK' => "\u{2755}", 'CHARACTER_EXCLAMATION_MARK' => "\u{2757}", 'CHARACTER_WAVY_DASH' => "\u{3030}\u{FE0F}", 'CHARACTER_COPYRIGHT' => "\u{00A9}\u{FE0F}", 'CHARACTER_REGISTERED' => "\u{00AE}\u{FE0F}", 'CHARACTER_TRADE_MARK' => "\u{2122}\u{FE0F}", 'CHARACTER_KEYCAP_HASH' => "\u{0023}\u{FE0F}\u{20E3}", 'CHARACTER_KEYCAP_ASTERISK' => "\u{002A}\u{FE0F}\u{20E3}", 'CHARACTER_KEYCAP_0' => "\u{0030}\u{FE0F}\u{20E3}", 'CHARACTER_KEYCAP_1' => "\u{0031}\u{FE0F}\u{20E3}", 'CHARACTER_KEYCAP_2' => "\u{0032}\u{FE0F}\u{20E3}", 'CHARACTER_KEYCAP_3' => "\u{0033}\u{FE0F}\u{20E3}", 'CHARACTER_KEYCAP_4' => "\u{0034}\u{FE0F}\u{20E3}", 'CHARACTER_KEYCAP_5' => "\u{0035}\u{FE0F}\u{20E3}", 'CHARACTER_KEYCAP_6' => "\u{0036}\u{FE0F}\u{20E3}", 'CHARACTER_KEYCAP_7' => "\u{0037}\u{FE0F}\u{20E3}", 'CHARACTER_KEYCAP_8' => "\u{0038}\u{FE0F}\u{20E3}", 'CHARACTER_KEYCAP_9' => "\u{0039}\u{FE0F}\u{20E3}", 'CHARACTER_KEYCAP_10' => "\u{1F51F}", 'CHARACTER_HUNDRED_POINTS' => "\u{1F4AF}", 'CHARACTER_INPUT_LATIN_UPPERCASE' => "\u{1F520}", 'CHARACTER_INPUT_LATIN_LOWERCASE' => "\u{1F521}", 'CHARACTER_INPUT_NUMBERS' => "\u{1F522}", 'CHARACTER_INPUT_SYMBOLS' => "\u{1F523}", 'CHARACTER_INPUT_LATIN_LETTERS' => "\u{1F524}", 'CHARACTER_A_BUTTON_BLOOD_TYPE' => "\u{1F170}\u{FE0F}", 'CHARACTER_AB_BUTTON_BLOOD_TYPE' => "\u{1F18E}", 'CHARACTER_B_BUTTON_BLOOD_TYPE' => "\u{1F171}\u{FE0F}", 'CHARACTER_CL_BUTTON' => "\u{1F191}", 'CHARACTER_COOL_BUTTON' => "\u{1F192}", 'CHARACTER_FREE_BUTTON' => "\u{1F193}", 'CHARACTER_INFORMATION' => "\u{2139}\u{FE0F}", 'CHARACTER_ID_BUTTON' => "\u{1F194}", 'CHARACTER_CIRCLED_M' => "\u{24C2}\u{FE0F}", 'CHARACTER_NEW_BUTTON' => "\u{1F195}", 'CHARACTER_NG_BUTTON' => "\u{1F196}", 'CHARACTER_O_BUTTON_BLOOD_TYPE' => "\u{1F17E}\u{FE0F}", 'CHARACTER_OK_BUTTON' => "\u{1F197}", 'CHARACTER_P_BUTTON' => "\u{1F17F}\u{FE0F}", 'CHARACTER_SOS_BUTTON' => "\u{1F198}", 'CHARACTER_UP_BUTTON' => "\u{1F199}", 'CHARACTER_VS_BUTTON' => "\u{1F19A}", 'CHARACTER_JAPANESE_HERE_BUTTON' => "\u{1F201}", 'CHARACTER_JAPANESE_SERVICE_CHARGE_BUTTON' => "\u{1F202}\u{FE0F}", 'CHARACTER_JAPANESE_MONTHLY_AMOUNT_BUTTON' => "\u{1F237}\u{FE0F}", 'CHARACTER_JAPANESE_NOT_FREE_OF_CHARGE_BUTTON' => "\u{1F236}", 'CHARACTER_JAPANESE_RESERVED_BUTTON' => "\u{1F22F}", 'CHARACTER_JAPANESE_BARGAIN_BUTTON' => "\u{1F250}", 'CHARACTER_JAPANESE_DISCOUNT_BUTTON' => "\u{1F239}", 'CHARACTER_JAPANESE_FREE_OF_CHARGE_BUTTON' => "\u{1F21A}", 'CHARACTER_JAPANESE_PROHIBITED_BUTTON' => "\u{1F232}", 'CHARACTER_JAPANESE_ACCEPTABLE_BUTTON' => "\u{1F251}", 'CHARACTER_JAPANESE_APPLICATION_BUTTON' => "\u{1F238}", 'CHARACTER_JAPANESE_PASSING_GRADE_BUTTON' => "\u{1F234}", 'CHARACTER_JAPANESE_VACANCY_BUTTON' => "\u{1F233}", 'CHARACTER_JAPANESE_CONGRATULATIONS_BUTTON' => "\u{3297}\u{FE0F}", 'CHARACTER_JAPANESE_SECRET_BUTTON' => "\u{3299}\u{FE0F}", 'CHARACTER_JAPANESE_OPEN_FOR_BUSINESS_BUTTON' => "\u{1F23A}", 'CHARACTER_JAPANESE_NO_VACANCY_BUTTON' => "\u{1F235}", 'CHARACTER_BLACK_SMALL_SQUARE' => "\u{25AA}\u{FE0F}", 'CHARACTER_WHITE_SMALL_SQUARE' => "\u{25AB}\u{FE0F}", 'CHARACTER_WHITE_MEDIUM_SQUARE' => "\u{25FB}\u{FE0F}", 'CHARACTER_BLACK_MEDIUM_SQUARE' => "\u{25FC}\u{FE0F}", 'CHARACTER_WHITE_MEDIUM_SMALL_SQUARE' => "\u{25FD}", 'CHARACTER_BLACK_MEDIUM_SMALL_SQUARE' => "\u{25FE}", 'CHARACTER_BLACK_LARGE_SQUARE' => "\u{2B1B}", 'CHARACTER_WHITE_LARGE_SQUARE' => "\u{2B1C}", 'CHARACTER_LARGE_ORANGE_DIAMOND' => "\u{1F536}", 'CHARACTER_LARGE_BLUE_DIAMOND' => "\u{1F537}", 'CHARACTER_SMALL_ORANGE_DIAMOND' => "\u{1F538}", 'CHARACTER_SMALL_BLUE_DIAMOND' => "\u{1F539}", 'CHARACTER_RED_TRIANGLE_POINTED_UP' => "\u{1F53A}", 'CHARACTER_RED_TRIANGLE_POINTED_DOWN' => "\u{1F53B}", 'CHARACTER_DIAMOND_WITH_A_DOT' => "\u{1F4A0}", 'CHARACTER_RADIO_BUTTON' => "\u{1F518}", 'CHARACTER_BLACK_SQUARE_BUTTON' => "\u{1F532}", 'CHARACTER_WHITE_SQUARE_BUTTON' => "\u{1F533}", 'CHARACTER_WHITE_CIRCLE' => "\u{26AA}", 'CHARACTER_BLACK_CIRCLE' => "\u{26AB}", 'CHARACTER_RED_CIRCLE' => "\u{1F534}", 'CHARACTER_BLUE_CIRCLE' => "\u{1F535}", 'CHARACTER_CHEQUERED_FLAG' => "\u{1F3C1}", 'CHARACTER_TRIANGULAR_FLAG' => "\u{1F6A9}", 'CHARACTER_CROSSED_FLAGS' => "\u{1F38C}", 'CHARACTER_BLACK_FLAG' => "\u{1F3F4}", 'CHARACTER_WHITE_FLAG' => "\u{1F3F3}\u{FE0F}", 'CHARACTER_RAINBOW_FLAG' => "\u{1F3F3}\u{FE0F}\u{200D}\u{1F308}", 'CHARACTER_PIRATE_FLAG' => "\u{1F3F4}\u{200D}\u{2620}\u{FE0F}", 'CHARACTER_FLAGS_FOR_ASCENSION_ISLAND' => "\u{1F1E6}\u{1F1E8}", 'CHARACTER_FLAGS_FOR_ANDORRA' => "\u{1F1E6}\u{1F1E9}", 'CHARACTER_FLAGS_FOR_UNITED_ARAB_EMIRATES' => "\u{1F1E6}\u{1F1EA}", 'CHARACTER_FLAGS_FOR_AFGHANISTAN' => "\u{1F1E6}\u{1F1EB}", 'CHARACTER_FLAGS_FOR_ANTIGUA_AND_BARBUDA' => "\u{1F1E6}\u{1F1EC}", 'CHARACTER_FLAGS_FOR_ANGUILLA' => "\u{1F1E6}\u{1F1EE}", 'CHARACTER_FLAGS_FOR_ALBANIA' => "\u{1F1E6}\u{1F1F1}", 'CHARACTER_FLAGS_FOR_ARMENIA' => "\u{1F1E6}\u{1F1F2}", 'CHARACTER_FLAGS_FOR_ANGOLA' => "\u{1F1E6}\u{1F1F4}", 'CHARACTER_FLAGS_FOR_ANTARCTICA' => "\u{1F1E6}\u{1F1F6}", 'CHARACTER_FLAGS_FOR_ARGENTINA' => "\u{1F1E6}\u{1F1F7}", 'CHARACTER_FLAGS_FOR_AMERICAN_SAMOA' => "\u{1F1E6}\u{1F1F8}", 'CHARACTER_FLAGS_FOR_AUSTRIA' => "\u{1F1E6}\u{1F1F9}", 'CHARACTER_FLAGS_FOR_AUSTRALIA' => "\u{1F1E6}\u{1F1FA}", 'CHARACTER_FLAGS_FOR_ARUBA' => "\u{1F1E6}\u{1F1FC}", 'CHARACTER_FLAGS_FOR_ALAND_ISLANDS' => "\u{1F1E6}\u{1F1FD}", 'CHARACTER_FLAGS_FOR_AZERBAIJAN' => "\u{1F1E6}\u{1F1FF}", 'CHARACTER_FLAGS_FOR_BOSNIA_AND_HERZEGOVINA' => "\u{1F1E7}\u{1F1E6}", 'CHARACTER_FLAGS_FOR_BARBADOS' => "\u{1F1E7}\u{1F1E7}", 'CHARACTER_FLAGS_FOR_BANGLADESH' => "\u{1F1E7}\u{1F1E9}", 'CHARACTER_FLAGS_FOR_BELGIUM' => "\u{1F1E7}\u{1F1EA}", 'CHARACTER_FLAGS_FOR_BURKINA_FASO' => "\u{1F1E7}\u{1F1EB}", 'CHARACTER_FLAGS_FOR_BULGARIA' => "\u{1F1E7}\u{1F1EC}", 'CHARACTER_FLAGS_FOR_BAHRAIN' => "\u{1F1E7}\u{1F1ED}", 'CHARACTER_FLAGS_FOR_BURUNDI' => "\u{1F1E7}\u{1F1EE}", 'CHARACTER_FLAGS_FOR_BENIN' => "\u{1F1E7}\u{1F1EF}", 'CHARACTER_FLAGS_FOR_ST_BARTHELEMY' => "\u{1F1E7}\u{1F1F1}", 'CHARACTER_FLAGS_FOR_BERMUDA' => "\u{1F1E7}\u{1F1F2}", 'CHARACTER_FLAGS_FOR_BRUNEI' => "\u{1F1E7}\u{1F1F3}", 'CHARACTER_FLAGS_FOR_BOLIVIA' => "\u{1F1E7}\u{1F1F4}", 'CHARACTER_FLAGS_FOR_CARIBBEAN_NETHERLANDS' => "\u{1F1E7}\u{1F1F6}", 'CHARACTER_FLAGS_FOR_BRAZIL' => "\u{1F1E7}\u{1F1F7}", 'CHARACTER_FLAGS_FOR_BAHAMAS' => "\u{1F1E7}\u{1F1F8}", 'CHARACTER_FLAGS_FOR_BHUTAN' => "\u{1F1E7}\u{1F1F9}", 'CHARACTER_FLAGS_FOR_BOUVET_ISLAND' => "\u{1F1E7}\u{1F1FB}", 'CHARACTER_FLAGS_FOR_BOTSWANA' => "\u{1F1E7}\u{1F1FC}", 'CHARACTER_FLAGS_FOR_BELARUS' => "\u{1F1E7}\u{1F1FE}", 'CHARACTER_FLAGS_FOR_BELIZE' => "\u{1F1E7}\u{1F1FF}", 'CHARACTER_FLAGS_FOR_CANADA' => "\u{1F1E8}\u{1F1E6}", 'CHARACTER_FLAGS_FOR_COCOS_KEELING_ISLANDS' => "\u{1F1E8}\u{1F1E8}", 'CHARACTER_FLAGS_FOR_CONGO_KINSHASA' => "\u{1F1E8}\u{1F1E9}", 'CHARACTER_FLAGS_FOR_CENTRAL_AFRICAN_REPUBLIC' => "\u{1F1E8}\u{1F1EB}", 'CHARACTER_FLAGS_FOR_CONGO_BRAZZAVILLE' => "\u{1F1E8}\u{1F1EC}", 'CHARACTER_FLAGS_FOR_SWITZERLAND' => "\u{1F1E8}\u{1F1ED}", 'CHARACTER_FLAGS_FOR_COTE_D_IVOIRE' => "\u{1F1E8}\u{1F1EE}", 'CHARACTER_FLAGS_FOR_COOK_ISLANDS' => "\u{1F1E8}\u{1F1F0}", 'CHARACTER_FLAGS_FOR_CHILE' => "\u{1F1E8}\u{1F1F1}", 'CHARACTER_FLAGS_FOR_CAMEROON' => "\u{1F1E8}\u{1F1F2}", 'CHARACTER_FLAGS_FOR_CHINA' => "\u{1F1E8}\u{1F1F3}", 'CHARACTER_FLAGS_FOR_COLOMBIA' => "\u{1F1E8}\u{1F1F4}", 'CHARACTER_FLAGS_FOR_CLIPPERTON_ISLAND' => "\u{1F1E8}\u{1F1F5}", 'CHARACTER_FLAGS_FOR_COSTA_RICA' => "\u{1F1E8}\u{1F1F7}", 'CHARACTER_FLAGS_FOR_CUBA' => "\u{1F1E8}\u{1F1FA}", 'CHARACTER_FLAGS_FOR_CAPE_VERDE' => "\u{1F1E8}\u{1F1FB}", 'CHARACTER_FLAGS_FOR_CURACAO' => "\u{1F1E8}\u{1F1FC}", 'CHARACTER_FLAGS_FOR_CHRISTMAS_ISLAND' => "\u{1F1E8}\u{1F1FD}", 'CHARACTER_FLAGS_FOR_CYPRUS' => "\u{1F1E8}\u{1F1FE}", 'CHARACTER_FLAGS_FOR_CZECHIA' => "\u{1F1E8}\u{1F1FF}", 'CHARACTER_FLAGS_FOR_GERMANY' => "\u{1F1E9}\u{1F1EA}", 'CHARACTER_FLAGS_FOR_DIEGO_GARCIA' => "\u{1F1E9}\u{1F1EC}", 'CHARACTER_FLAGS_FOR_DJIBOUTI' => "\u{1F1E9}\u{1F1EF}", 'CHARACTER_FLAGS_FOR_DENMARK' => "\u{1F1E9}\u{1F1F0}", 'CHARACTER_FLAGS_FOR_DOMINICA' => "\u{1F1E9}\u{1F1F2}", 'CHARACTER_FLAGS_FOR_DOMINICAN_REPUBLIC' => "\u{1F1E9}\u{1F1F4}", 'CHARACTER_FLAGS_FOR_ALGERIA' => "\u{1F1E9}\u{1F1FF}", 'CHARACTER_FLAGS_FOR_CEUTA_AND_MELILLA' => "\u{1F1EA}\u{1F1E6}", 'CHARACTER_FLAGS_FOR_ECUADOR' => "\u{1F1EA}\u{1F1E8}", 'CHARACTER_FLAGS_FOR_ESTONIA' => "\u{1F1EA}\u{1F1EA}", 'CHARACTER_FLAGS_FOR_EGYPT' => "\u{1F1EA}\u{1F1EC}", 'CHARACTER_FLAGS_FOR_WESTERN_SAHARA' => "\u{1F1EA}\u{1F1ED}", 'CHARACTER_FLAGS_FOR_ERITREA' => "\u{1F1EA}\u{1F1F7}", 'CHARACTER_FLAGS_FOR_SPAIN' => "\u{1F1EA}\u{1F1F8}", 'CHARACTER_FLAGS_FOR_ETHIOPIA' => "\u{1F1EA}\u{1F1F9}", 'CHARACTER_FLAGS_FOR_EUROPEAN_UNION' => "\u{1F1EA}\u{1F1FA}", 'CHARACTER_FLAGS_FOR_FINLAND' => "\u{1F1EB}\u{1F1EE}", 'CHARACTER_FLAGS_FOR_FIJI' => "\u{1F1EB}\u{1F1EF}", 'CHARACTER_FLAGS_FOR_FALKLAND_ISLANDS' => "\u{1F1EB}\u{1F1F0}", 'CHARACTER_FLAGS_FOR_MICRONESIA' => "\u{1F1EB}\u{1F1F2}", 'CHARACTER_FLAGS_FOR_FAROE_ISLANDS' => "\u{1F1EB}\u{1F1F4}", 'CHARACTER_FLAGS_FOR_FRANCE' => "\u{1F1EB}\u{1F1F7}", 'CHARACTER_FLAGS_FOR_GABON' => "\u{1F1EC}\u{1F1E6}", 'CHARACTER_FLAGS_FOR_UNITED_KINGDOM' => "\u{1F1EC}\u{1F1E7}", 'CHARACTER_FLAGS_FOR_GRENADA' => "\u{1F1EC}\u{1F1E9}", 'CHARACTER_FLAGS_FOR_GEORGIA' => "\u{1F1EC}\u{1F1EA}", 'CHARACTER_FLAGS_FOR_FRENCH_GUIANA' => "\u{1F1EC}\u{1F1EB}", 'CHARACTER_FLAGS_FOR_GUERNSEY' => "\u{1F1EC}\u{1F1EC}", 'CHARACTER_FLAGS_FOR_GHANA' => "\u{1F1EC}\u{1F1ED}", 'CHARACTER_FLAGS_FOR_GIBRALTAR' => "\u{1F1EC}\u{1F1EE}", 'CHARACTER_FLAGS_FOR_GREENLAND' => "\u{1F1EC}\u{1F1F1}", 'CHARACTER_FLAGS_FOR_GAMBIA' => "\u{1F1EC}\u{1F1F2}", 'CHARACTER_FLAGS_FOR_GUINEA' => "\u{1F1EC}\u{1F1F3}", 'CHARACTER_FLAGS_FOR_GUADELOUPE' => "\u{1F1EC}\u{1F1F5}", 'CHARACTER_FLAGS_FOR_EQUATORIAL_GUINEA' => "\u{1F1EC}\u{1F1F6}", 'CHARACTER_FLAGS_FOR_GREECE' => "\u{1F1EC}\u{1F1F7}", 'CHARACTER_FLAGS_FOR_SOUTH_GEORGIA_AND_SOUTH_SANDWICH_ISLANDS' => "\u{1F1EC}\u{1F1F8}", 'CHARACTER_FLAGS_FOR_GUATEMALA' => "\u{1F1EC}\u{1F1F9}", 'CHARACTER_FLAGS_FOR_GUAM' => "\u{1F1EC}\u{1F1FA}", 'CHARACTER_FLAGS_FOR_GUINEA_BISSAU' => "\u{1F1EC}\u{1F1FC}", 'CHARACTER_FLAGS_FOR_GUYANA' => "\u{1F1EC}\u{1F1FE}", 'CHARACTER_FLAGS_FOR_HONG_KONG_SAR_CHINA' => "\u{1F1ED}\u{1F1F0}", 'CHARACTER_FLAGS_FOR_HEARD_AND_MCDONALD_ISLANDS' => "\u{1F1ED}\u{1F1F2}", 'CHARACTER_FLAGS_FOR_HONDURAS' => "\u{1F1ED}\u{1F1F3}", 'CHARACTER_FLAGS_FOR_CROATIA' => "\u{1F1ED}\u{1F1F7}", 'CHARACTER_FLAGS_FOR_HAITI' => "\u{1F1ED}\u{1F1F9}", 'CHARACTER_FLAGS_FOR_HUNGARY' => "\u{1F1ED}\u{1F1FA}", 'CHARACTER_FLAGS_FOR_CANARY_ISLANDS' => "\u{1F1EE}\u{1F1E8}", 'CHARACTER_FLAGS_FOR_INDONESIA' => "\u{1F1EE}\u{1F1E9}", 'CHARACTER_FLAGS_FOR_IRELAND' => "\u{1F1EE}\u{1F1EA}", 'CHARACTER_FLAGS_FOR_ISRAEL' => "\u{1F1EE}\u{1F1F1}", 'CHARACTER_FLAGS_FOR_ISLE_OF_MAN' => "\u{1F1EE}\u{1F1F2}", 'CHARACTER_FLAGS_FOR_INDIA' => "\u{1F1EE}\u{1F1F3}", 'CHARACTER_FLAGS_FOR_BRITISH_INDIAN_OCEAN_TERRITORY' => "\u{1F1EE}\u{1F1F4}", 'CHARACTER_FLAGS_FOR_IRAQ' => "\u{1F1EE}\u{1F1F6}", 'CHARACTER_FLAGS_FOR_IRAN' => "\u{1F1EE}\u{1F1F7}", 'CHARACTER_FLAGS_FOR_ICELAND' => "\u{1F1EE}\u{1F1F8}", 'CHARACTER_FLAGS_FOR_ITALY' => "\u{1F1EE}\u{1F1F9}", 'CHARACTER_FLAGS_FOR_JERSEY' => "\u{1F1EF}\u{1F1EA}", 'CHARACTER_FLAGS_FOR_JAMAICA' => "\u{1F1EF}\u{1F1F2}", 'CHARACTER_FLAGS_FOR_JORDAN' => "\u{1F1EF}\u{1F1F4}", 'CHARACTER_FLAGS_FOR_JAPAN' => "\u{1F1EF}\u{1F1F5}", 'CHARACTER_FLAGS_FOR_KENYA' => "\u{1F1F0}\u{1F1EA}", 'CHARACTER_FLAGS_FOR_KYRGYZSTAN' => "\u{1F1F0}\u{1F1EC}", 'CHARACTER_FLAGS_FOR_CAMBODIA' => "\u{1F1F0}\u{1F1ED}", 'CHARACTER_FLAGS_FOR_KIRIBATI' => "\u{1F1F0}\u{1F1EE}", 'CHARACTER_FLAGS_FOR_COMOROS' => "\u{1F1F0}\u{1F1F2}", 'CHARACTER_FLAGS_FOR_ST_KITTS_AND_NEVIS' => "\u{1F1F0}\u{1F1F3}", 'CHARACTER_FLAGS_FOR_NORTH_KOREA' => "\u{1F1F0}\u{1F1F5}", 'CHARACTER_FLAGS_FOR_SOUTH_KOREA' => "\u{1F1F0}\u{1F1F7}", 'CHARACTER_FLAGS_FOR_KUWAIT' => "\u{1F1F0}\u{1F1FC}", 'CHARACTER_FLAGS_FOR_CAYMAN_ISLANDS' => "\u{1F1F0}\u{1F1FE}", 'CHARACTER_FLAGS_FOR_KAZAKHSTAN' => "\u{1F1F0}\u{1F1FF}", 'CHARACTER_FLAGS_FOR_LAOS' => "\u{1F1F1}\u{1F1E6}", 'CHARACTER_FLAGS_FOR_LEBANON' => "\u{1F1F1}\u{1F1E7}", 'CHARACTER_FLAGS_FOR_ST_LUCIA' => "\u{1F1F1}\u{1F1E8}", 'CHARACTER_FLAGS_FOR_LIECHTENSTEIN' => "\u{1F1F1}\u{1F1EE}", 'CHARACTER_FLAGS_FOR_SRI_LANKA' => "\u{1F1F1}\u{1F1F0}", 'CHARACTER_FLAGS_FOR_LIBERIA' => "\u{1F1F1}\u{1F1F7}", 'CHARACTER_FLAGS_FOR_LESOTHO' => "\u{1F1F1}\u{1F1F8}", 'CHARACTER_FLAGS_FOR_LITHUANIA' => "\u{1F1F1}\u{1F1F9}", 'CHARACTER_FLAGS_FOR_LUXEMBOURG' => "\u{1F1F1}\u{1F1FA}", 'CHARACTER_FLAGS_FOR_LATVIA' => "\u{1F1F1}\u{1F1FB}", 'CHARACTER_FLAGS_FOR_LIBYA' => "\u{1F1F1}\u{1F1FE}", 'CHARACTER_FLAGS_FOR_MOROCCO' => "\u{1F1F2}\u{1F1E6}", 'CHARACTER_FLAGS_FOR_MONACO' => "\u{1F1F2}\u{1F1E8}", 'CHARACTER_FLAGS_FOR_MOLDOVA' => "\u{1F1F2}\u{1F1E9}", 'CHARACTER_FLAGS_FOR_MONTENEGRO' => "\u{1F1F2}\u{1F1EA}", 'CHARACTER_FLAGS_FOR_ST_MARTIN' => "\u{1F1F2}\u{1F1EB}", 'CHARACTER_FLAGS_FOR_MADAGASCAR' => "\u{1F1F2}\u{1F1EC}", 'CHARACTER_FLAGS_FOR_MARSHALL_ISLANDS' => "\u{1F1F2}\u{1F1ED}", 'CHARACTER_FLAGS_FOR_MACEDONIA' => "\u{1F1F2}\u{1F1F0}", 'CHARACTER_FLAGS_FOR_MALI' => "\u{1F1F2}\u{1F1F1}", 'CHARACTER_FLAGS_FOR_MYANMAR_BURMA' => "\u{1F1F2}\u{1F1F2}", 'CHARACTER_FLAGS_FOR_MONGOLIA' => "\u{1F1F2}\u{1F1F3}", 'CHARACTER_FLAGS_FOR_MACAU_SAR_CHINA' => "\u{1F1F2}\u{1F1F4}", 'CHARACTER_FLAGS_FOR_NORTHERN_MARIANA_ISLANDS' => "\u{1F1F2}\u{1F1F5}", 'CHARACTER_FLAGS_FOR_MARTINIQUE' => "\u{1F1F2}\u{1F1F6}", 'CHARACTER_FLAGS_FOR_MAURITANIA' => "\u{1F1F2}\u{1F1F7}", 'CHARACTER_FLAGS_FOR_MONTSERRAT' => "\u{1F1F2}\u{1F1F8}", 'CHARACTER_FLAGS_FOR_MALTA' => "\u{1F1F2}\u{1F1F9}", 'CHARACTER_FLAGS_FOR_MAURITIUS' => "\u{1F1F2}\u{1F1FA}", 'CHARACTER_FLAGS_FOR_MALDIVES' => "\u{1F1F2}\u{1F1FB}", 'CHARACTER_FLAGS_FOR_MALAWI' => "\u{1F1F2}\u{1F1FC}", 'CHARACTER_FLAGS_FOR_MEXICO' => "\u{1F1F2}\u{1F1FD}", 'CHARACTER_FLAGS_FOR_MALAYSIA' => "\u{1F1F2}\u{1F1FE}", 'CHARACTER_FLAGS_FOR_MOZAMBIQUE' => "\u{1F1F2}\u{1F1FF}", 'CHARACTER_FLAGS_FOR_NAMIBIA' => "\u{1F1F3}\u{1F1E6}", 'CHARACTER_FLAGS_FOR_NEW_CALEDONIA' => "\u{1F1F3}\u{1F1E8}", 'CHARACTER_FLAGS_FOR_NIGER' => "\u{1F1F3}\u{1F1EA}", 'CHARACTER_FLAGS_FOR_NORFOLK_ISLAND' => "\u{1F1F3}\u{1F1EB}", 'CHARACTER_FLAGS_FOR_NIGERIA' => "\u{1F1F3}\u{1F1EC}", 'CHARACTER_FLAGS_FOR_NICARAGUA' => "\u{1F1F3}\u{1F1EE}", 'CHARACTER_FLAGS_FOR_NETHERLANDS' => "\u{1F1F3}\u{1F1F1}", 'CHARACTER_FLAGS_FOR_NORWAY' => "\u{1F1F3}\u{1F1F4}", 'CHARACTER_FLAGS_FOR_NEPAL' => "\u{1F1F3}\u{1F1F5}", 'CHARACTER_FLAGS_FOR_NAURU' => "\u{1F1F3}\u{1F1F7}", 'CHARACTER_FLAGS_FOR_NIUE' => "\u{1F1F3}\u{1F1FA}", 'CHARACTER_FLAGS_FOR_NEW_ZEALAND' => "\u{1F1F3}\u{1F1FF}", 'CHARACTER_FLAGS_FOR_OMAN' => "\u{1F1F4}\u{1F1F2}", 'CHARACTER_FLAGS_FOR_PANAMA' => "\u{1F1F5}\u{1F1E6}", 'CHARACTER_FLAGS_FOR_PERU' => "\u{1F1F5}\u{1F1EA}", 'CHARACTER_FLAGS_FOR_FRENCH_POLYNESIA' => "\u{1F1F5}\u{1F1EB}", 'CHARACTER_FLAGS_FOR_PAPUA_NEW_GUINEA' => "\u{1F1F5}\u{1F1EC}", 'CHARACTER_FLAGS_FOR_PHILIPPINES' => "\u{1F1F5}\u{1F1ED}", 'CHARACTER_FLAGS_FOR_PAKISTAN' => "\u{1F1F5}\u{1F1F0}", 'CHARACTER_FLAGS_FOR_POLAND' => "\u{1F1F5}\u{1F1F1}", 'CHARACTER_FLAGS_FOR_ST_PIERRE_AND_MIQUELON' => "\u{1F1F5}\u{1F1F2}", 'CHARACTER_FLAGS_FOR_PITCAIRN_ISLANDS' => "\u{1F1F5}\u{1F1F3}", 'CHARACTER_FLAGS_FOR_PUERTO_RICO' => "\u{1F1F5}\u{1F1F7}", 'CHARACTER_FLAGS_FOR_PALESTINIAN_TERRITORIES' => "\u{1F1F5}\u{1F1F8}", 'CHARACTER_FLAGS_FOR_PORTUGAL' => "\u{1F1F5}\u{1F1F9}", 'CHARACTER_FLAGS_FOR_PALAU' => "\u{1F1F5}\u{1F1FC}", 'CHARACTER_FLAGS_FOR_PARAGUAY' => "\u{1F1F5}\u{1F1FE}", 'CHARACTER_FLAGS_FOR_QATAR' => "\u{1F1F6}\u{1F1E6}", 'CHARACTER_FLAGS_FOR_REUNION' => "\u{1F1F7}\u{1F1EA}", 'CHARACTER_FLAGS_FOR_ROMANIA' => "\u{1F1F7}\u{1F1F4}", 'CHARACTER_FLAGS_FOR_SERBIA' => "\u{1F1F7}\u{1F1F8}", 'CHARACTER_FLAGS_FOR_RUSSIA' => "\u{1F1F7}\u{1F1FA}", 'CHARACTER_FLAGS_FOR_RWANDA' => "\u{1F1F7}\u{1F1FC}", 'CHARACTER_FLAGS_FOR_SAUDI_ARABIA' => "\u{1F1F8}\u{1F1E6}", 'CHARACTER_FLAGS_FOR_SOLOMON_ISLANDS' => "\u{1F1F8}\u{1F1E7}", 'CHARACTER_FLAGS_FOR_SEYCHELLES' => "\u{1F1F8}\u{1F1E8}", 'CHARACTER_FLAGS_FOR_SUDAN' => "\u{1F1F8}\u{1F1E9}", 'CHARACTER_FLAGS_FOR_SWEDEN' => "\u{1F1F8}\u{1F1EA}", 'CHARACTER_FLAGS_FOR_SINGAPORE' => "\u{1F1F8}\u{1F1EC}", 'CHARACTER_FLAGS_FOR_ST_HELENA' => "\u{1F1F8}\u{1F1ED}", 'CHARACTER_FLAGS_FOR_SLOVENIA' => "\u{1F1F8}\u{1F1EE}", 'CHARACTER_FLAGS_FOR_SVALBARD_AND_JAN_MAYEN' => "\u{1F1F8}\u{1F1EF}", 'CHARACTER_FLAGS_FOR_SLOVAKIA' => "\u{1F1F8}\u{1F1F0}", 'CHARACTER_FLAGS_FOR_SIERRA_LEONE' => "\u{1F1F8}\u{1F1F1}", 'CHARACTER_FLAGS_FOR_SAN_MARINO' => "\u{1F1F8}\u{1F1F2}", 'CHARACTER_FLAGS_FOR_SENEGAL' => "\u{1F1F8}\u{1F1F3}", 'CHARACTER_FLAGS_FOR_SOMALIA' => "\u{1F1F8}\u{1F1F4}", 'CHARACTER_FLAGS_FOR_SURINAME' => "\u{1F1F8}\u{1F1F7}", 'CHARACTER_FLAGS_FOR_SOUTH_SUDAN' => "\u{1F1F8}\u{1F1F8}", 'CHARACTER_FLAGS_FOR_SAO_TOME_AND_PRINCIPE' => "\u{1F1F8}\u{1F1F9}", 'CHARACTER_FLAGS_FOR_EL_SALVADOR' => "\u{1F1F8}\u{1F1FB}", 'CHARACTER_FLAGS_FOR_SINT_MAARTEN' => "\u{1F1F8}\u{1F1FD}", 'CHARACTER_FLAGS_FOR_SYRIA' => "\u{1F1F8}\u{1F1FE}", 'CHARACTER_FLAGS_FOR_SWAZILAND' => "\u{1F1F8}\u{1F1FF}", 'CHARACTER_FLAGS_FOR_TRISTAN_DA_CUNHA' => "\u{1F1F9}\u{1F1E6}", 'CHARACTER_FLAGS_FOR_TURKS_AND_CAICOS_ISLANDS' => "\u{1F1F9}\u{1F1E8}", 'CHARACTER_FLAGS_FOR_CHAD' => "\u{1F1F9}\u{1F1E9}", 'CHARACTER_FLAGS_FOR_FRENCH_SOUTHERN_TERRITORIES' => "\u{1F1F9}\u{1F1EB}", 'CHARACTER_FLAGS_FOR_TOGO' => "\u{1F1F9}\u{1F1EC}", 'CHARACTER_FLAGS_FOR_THAILAND' => "\u{1F1F9}\u{1F1ED}", 'CHARACTER_FLAGS_FOR_TAJIKISTAN' => "\u{1F1F9}\u{1F1EF}", 'CHARACTER_FLAGS_FOR_TOKELAU' => "\u{1F1F9}\u{1F1F0}", 'CHARACTER_FLAGS_FOR_TIMOR_LESTE' => "\u{1F1F9}\u{1F1F1}", 'CHARACTER_FLAGS_FOR_TURKMENISTAN' => "\u{1F1F9}\u{1F1F2}", 'CHARACTER_FLAGS_FOR_TUNISIA' => "\u{1F1F9}\u{1F1F3}", 'CHARACTER_FLAGS_FOR_TONGA' => "\u{1F1F9}\u{1F1F4}", 'CHARACTER_FLAGS_FOR_TURKEY' => "\u{1F1F9}\u{1F1F7}", 'CHARACTER_FLAGS_FOR_TRINIDAD_AND_TOBAGO' => "\u{1F1F9}\u{1F1F9}", 'CHARACTER_FLAGS_FOR_TUVALU' => "\u{1F1F9}\u{1F1FB}", 'CHARACTER_FLAGS_FOR_TAIWAN' => "\u{1F1F9}\u{1F1FC}", 'CHARACTER_FLAGS_FOR_TANZANIA' => "\u{1F1F9}\u{1F1FF}", 'CHARACTER_FLAGS_FOR_UKRAINE' => "\u{1F1FA}\u{1F1E6}", 'CHARACTER_FLAGS_FOR_UGANDA' => "\u{1F1FA}\u{1F1EC}", 'CHARACTER_FLAGS_FOR_US_OUTLYING_ISLANDS' => "\u{1F1FA}\u{1F1F2}", 'CHARACTER_FLAGS_FOR_UNITED_NATIONS' => "\u{1F1FA}\u{1F1F3}", 'CHARACTER_FLAGS_FOR_UNITED_STATES' => "\u{1F1FA}\u{1F1F8}", 'CHARACTER_FLAGS_FOR_URUGUAY' => "\u{1F1FA}\u{1F1FE}", 'CHARACTER_FLAGS_FOR_UZBEKISTAN' => "\u{1F1FA}\u{1F1FF}", 'CHARACTER_FLAGS_FOR_VATICAN_CITY' => "\u{1F1FB}\u{1F1E6}", 'CHARACTER_FLAGS_FOR_ST_VINCENT_AND_GRENADINES' => "\u{1F1FB}\u{1F1E8}", 'CHARACTER_FLAGS_FOR_VENEZUELA' => "\u{1F1FB}\u{1F1EA}", 'CHARACTER_FLAGS_FOR_BRITISH_VIRGIN_ISLANDS' => "\u{1F1FB}\u{1F1EC}", 'CHARACTER_FLAGS_FOR_US_VIRGIN_ISLANDS' => "\u{1F1FB}\u{1F1EE}", 'CHARACTER_FLAGS_FOR_VIETNAM' => "\u{1F1FB}\u{1F1F3}", 'CHARACTER_FLAGS_FOR_VANUATU' => "\u{1F1FB}\u{1F1FA}", 'CHARACTER_FLAGS_FOR_WALLIS_AND_FUTUNA' => "\u{1F1FC}\u{1F1EB}", 'CHARACTER_FLAGS_FOR_SAMOA' => "\u{1F1FC}\u{1F1F8}", 'CHARACTER_FLAGS_FOR_KOSOVO' => "\u{1F1FD}\u{1F1F0}", 'CHARACTER_FLAGS_FOR_YEMEN' => "\u{1F1FE}\u{1F1EA}", 'CHARACTER_FLAGS_FOR_MAYOTTE' => "\u{1F1FE}\u{1F1F9}", 'CHARACTER_FLAGS_FOR_SOUTH_AFRICA' => "\u{1F1FF}\u{1F1E6}", 'CHARACTER_FLAGS_FOR_ZAMBIA' => "\u{1F1FF}\u{1F1F2}", 'CHARACTER_FLAGS_FOR_ZIMBABWE' => "\u{1F1FF}\u{1F1FC}", 'CHARACTER_FLAGS_FOR_ENGLAND' => "\u{1F3F4}\u{E0067}\u{E0062}\u{E0065}\u{E006E}\u{E0067}\u{E007F}", 'CHARACTER_FLAGS_FOR_SCOTLAND' => "\u{1F3F4}\u{E0067}\u{E0062}\u{E0073}\u{E0063}\u{E0074}\u{E007F}", 'CHARACTER_FLAGS_FOR_WALES' => "\u{1F3F4}\u{E0067}\u{E0062}\u{E0077}\u{E006C}\u{E0073}\u{E007F}"]; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/encodings.php b/includes/libraries/anti-xss-master/src/voku/helper/data/encodings.php new file mode 100644 index 000000000..87d907048 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/encodings.php @@ -0,0 +1 @@ + 0, "\x00" => 0, "\x01" => 1, "\x02" => 2, "\x03" => 3, "\x04" => 4, "\x05" => 5, "\x06" => 6, "\x07" => 7, "\x08" => 8, "\x09" => 9, "\x0A" => 10, "\x0B" => 11, "\x0C" => 12, "\x0D" => 13, "\x0E" => 14, "\x0F" => 15, "\x10" => 16, "\x11" => 17, "\x12" => 18, "\x13" => 19, "\x14" => 20, "\x15" => 21, "\x16" => 22, "\x17" => 23, "\x18" => 24, "\x19" => 25, "\x1A" => 26, "\x1B" => 27, "\x1C" => 28, "\x1D" => 29, "\x1E" => 30, "\x1F" => 31, "\x20" => 32, "\x21" => 33, "\x22" => 34, "\x23" => 35, "\x24" => 36, "\x25" => 37, "\x26" => 38, "\x27" => 39, "\x28" => 40, "\x29" => 41, "\x2A" => 42, "\x2B" => 43, "\x2C" => 44, "\x2D" => 45, "\x2E" => 46, "\x2F" => 47, "\x30" => 48, "\x31" => 49, "\x32" => 50, "\x33" => 51, "\x34" => 52, "\x35" => 53, "\x36" => 54, "\x37" => 55, "\x38" => 56, "\x39" => 57, "\x3A" => 58, "\x3B" => 59, "\x3C" => 60, "\x3D" => 61, "\x3E" => 62, "\x3F" => 63, "\x40" => 64, "\x41" => 65, "\x42" => 66, "\x43" => 67, "\x44" => 68, "\x45" => 69, "\x46" => 70, "\x47" => 71, "\x48" => 72, "\x49" => 73, "\x4A" => 74, "\x4B" => 75, "\x4C" => 76, "\x4D" => 77, "\x4E" => 78, "\x4F" => 79, "\x50" => 80, "\x51" => 81, "\x52" => 82, "\x53" => 83, "\x54" => 84, "\x55" => 85, "\x56" => 86, "\x57" => 87, "\x58" => 88, "\x59" => 89, "\x5A" => 90, "\x5B" => 91, "\x5C" => 92, "\x5D" => 93, "\x5E" => 94, "\x5F" => 95, "\x60" => 96, "\x61" => 97, "\x62" => 98, "\x63" => 99, "\x64" => 100, "\x65" => 101, "\x66" => 102, "\x67" => 103, "\x68" => 104, "\x69" => 105, "\x6A" => 106, "\x6B" => 107, "\x6C" => 108, "\x6D" => 109, "\x6E" => 110, "\x6F" => 111, "\x70" => 112, "\x71" => 113, "\x72" => 114, "\x73" => 115, "\x74" => 116, "\x75" => 117, "\x76" => 118, "\x77" => 119, "\x78" => 120, "\x79" => 121, "\x7A" => 122, "\x7B" => 123, "\x7C" => 124, "\x7D" => 125, "\x7E" => 126, "\x7F" => 127, "\x80" => 128, "\x81" => 129, "\x82" => 130, "\x83" => 131, "\x84" => 132, "\x85" => 133, "\x86" => 134, "\x87" => 135, "\x88" => 136, "\x89" => 137, "\x8A" => 138, "\x8B" => 139, "\x8C" => 140, "\x8D" => 141, "\x8E" => 142, "\x8F" => 143, "\x90" => 144, "\x91" => 145, "\x92" => 146, "\x93" => 147, "\x94" => 148, "\x95" => 149, "\x96" => 150, "\x97" => 151, "\x98" => 152, "\x99" => 153, "\x9A" => 154, "\x9B" => 155, "\x9C" => 156, "\x9D" => 157, "\x9E" => 158, "\x9F" => 159, "\xA0" => 160, "\xA1" => 161, "\xA2" => 162, "\xA3" => 163, "\xA4" => 164, "\xA5" => 165, "\xA6" => 166, "\xA7" => 167, "\xA8" => 168, "\xA9" => 169, "\xAA" => 170, "\xAB" => 171, "\xAC" => 172, "\xAD" => 173, "\xAE" => 174, "\xAF" => 175, "\xB0" => 176, "\xB1" => 177, "\xB2" => 178, "\xB3" => 179, "\xB4" => 180, "\xB5" => 181, "\xB6" => 182, "\xB7" => 183, "\xB8" => 184, "\xB9" => 185, "\xBA" => 186, "\xBB" => 187, "\xBC" => 188, "\xBD" => 189, "\xBE" => 190, "\xBF" => 191, "\xC0" => 192, "\xC1" => 193, "\xC2" => 194, "\xC3" => 195, "\xC4" => 196, "\xC5" => 197, "\xC6" => 198, "\xC7" => 199, "\xC8" => 200, "\xC9" => 201, "\xCA" => 202, "\xCB" => 203, "\xCC" => 204, "\xCD" => 205, "\xCE" => 206, "\xCF" => 207, "\xD0" => 208, "\xD1" => 209, "\xD2" => 210, "\xD3" => 211, "\xD4" => 212, "\xD5" => 213, "\xD6" => 214, "\xD7" => 215, "\xD8" => 216, "\xD9" => 217, "\xDA" => 218, "\xDB" => 219, "\xDC" => 220, "\xDD" => 221, "\xDE" => 222, "\xDF" => 223, "\xE0" => 224, "\xE1" => 225, "\xE2" => 226, "\xE3" => 227, "\xE4" => 228, "\xE5" => 229, "\xE6" => 230, "\xE7" => 231, "\xE8" => 232, "\xE9" => 233, "\xEA" => 234, "\xEB" => 235, "\xEC" => 236, "\xED" => 237, "\xEE" => 238, "\xEF" => 239, "\xF0" => 240, "\xF1" => 241, "\xF2" => 242, "\xF3" => 243, "\xF4" => 244, "\xF5" => 245, "\xF6" => 246, "\xF7" => 247, "\xF8" => 248, "\xF9" => 249, "\xFA" => 250, "\xFB" => 251, "\xFC" => 252, "\xFD" => 253, "\xFE" => 254, "\xFF" => 255]; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/transliterator_list.php b/includes/libraries/anti-xss-master/src/voku/helper/data/transliterator_list.php new file mode 100644 index 000000000..91ddf13c6 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/transliterator_list.php @@ -0,0 +1 @@ + 'ASCII-Latin', 1 => 'Accents-Any', 2 => 'Amharic-Latin/BGN', 3 => 'Any-Accents', 4 => 'Any-Publishing', 5 => 'Arab-Latn', 6 => 'Arabic-Latin', 7 => 'Arabic-Latin/BGN', 8 => 'Armenian-Latin', 9 => 'Armenian-Latin/BGN', 10 => 'Armn-Latn', 11 => 'Azerbaijani-Latin/BGN', 12 => 'Belarusian-Latin/BGN', 13 => 'Beng-Arab', 14 => 'Beng-Deva', 15 => 'Beng-Gujr', 16 => 'Beng-Guru', 17 => 'Beng-Knda', 18 => 'Beng-Latn', 19 => 'Beng-Mlym', 20 => 'Beng-Orya', 21 => 'Beng-Taml', 22 => 'Beng-Telu', 23 => 'Beng-ur', 24 => 'Bengali-Arabic', 25 => 'Bengali-Devanagari', 26 => 'Bengali-Gujarati', 27 => 'Bengali-Gurmukhi', 28 => 'Bengali-Kannada', 29 => 'Bengali-Latin', 30 => 'Bengali-Malayalam', 31 => 'Bengali-Oriya', 32 => 'Bengali-Tamil', 33 => 'Bengali-Telugu', 34 => 'Bopo-Latn', 35 => 'Bopomofo-Latin', 36 => 'Bulgarian-Latin/BGN', 37 => 'Cyrillic-Latin', 38 => 'Cyrl-Latn', 39 => 'Deva-Arab', 40 => 'Deva-Beng', 41 => 'Deva-Gujr', 42 => 'Deva-Guru', 43 => 'Deva-Knda', 44 => 'Deva-Latn', 45 => 'Deva-Mlym', 46 => 'Deva-Orya', 47 => 'Deva-Taml', 48 => 'Deva-Telu', 49 => 'Deva-ur', 50 => 'Devanagari-Arabic', 51 => 'Devanagari-Bengali', 52 => 'Devanagari-Gujarati', 53 => 'Devanagari-Gurmukhi', 54 => 'Devanagari-Kannada', 55 => 'Devanagari-Latin', 56 => 'Devanagari-Malayalam', 57 => 'Devanagari-Oriya', 58 => 'Devanagari-Tamil', 59 => 'Devanagari-Telugu', 60 => 'Digit-Tone', 61 => 'Fullwidth-Halfwidth', 62 => 'Geor-Latn', 63 => 'Georgian-Latin', 64 => 'Georgian-Latin/BGN', 65 => 'Greek-Latin', 66 => 'Greek-Latin/BGN', 67 => 'Greek-Latin/UNGEGN', 68 => 'Grek-Latn', 69 => 'Grek-Latn/UNGEGN', 70 => 'Gujarati-Arabic', 71 => 'Gujarati-Bengali', 72 => 'Gujarati-Devanagari', 73 => 'Gujarati-Gurmukhi', 74 => 'Gujarati-Kannada', 75 => 'Gujarati-Latin', 76 => 'Gujarati-Malayalam', 77 => 'Gujarati-Oriya', 78 => 'Gujarati-Tamil', 79 => 'Gujarati-Telugu', 80 => 'Gujr-Arab', 81 => 'Gujr-Beng', 82 => 'Gujr-Deva', 83 => 'Gujr-Guru', 84 => 'Gujr-Knda', 85 => 'Gujr-Latn', 86 => 'Gujr-Mlym', 87 => 'Gujr-Orya', 88 => 'Gujr-Taml', 89 => 'Gujr-Telu', 90 => 'Gujr-ur', 91 => 'Gurmukhi-Arabic', 92 => 'Gurmukhi-Bengali', 93 => 'Gurmukhi-Devanagari', 94 => 'Gurmukhi-Gujarati', 95 => 'Gurmukhi-Kannada', 96 => 'Gurmukhi-Latin', 97 => 'Gurmukhi-Malayalam', 98 => 'Gurmukhi-Oriya', 99 => 'Gurmukhi-Tamil', 100 => 'Gurmukhi-Telugu', 101 => 'Guru-Arab', 102 => 'Guru-Beng', 103 => 'Guru-Deva', 104 => 'Guru-Gujr', 105 => 'Guru-Knda', 106 => 'Guru-Latn', 107 => 'Guru-Mlym', 108 => 'Guru-Orya', 109 => 'Guru-Taml', 110 => 'Guru-Telu', 111 => 'Guru-ur', 112 => 'Halfwidth-Fullwidth', 113 => 'Han-Latin', 114 => 'Han-Latin/Names', 115 => 'Hang-Latn', 116 => 'Hangul-Latin', 117 => 'Hani-Latn', 118 => 'Hans-Hant', 119 => 'Hant-Hans', 120 => 'Hebr-Latn', 121 => 'Hebrew-Latin', 122 => 'Hebrew-Latin/BGN', 123 => 'Hira-Kana', 124 => 'Hira-Latn', 125 => 'Hiragana-Katakana', 126 => 'Hiragana-Latin', 127 => 'IPA-XSampa', 128 => 'Jamo-Latin', 129 => 'Jamo-Latn', 130 => 'Kana-Hira', 131 => 'Kana-Latn', 132 => 'Kannada-Arabic', 133 => 'Kannada-Bengali', 134 => 'Kannada-Devanagari', 135 => 'Kannada-Gujarati', 136 => 'Kannada-Gurmukhi', 137 => 'Kannada-Latin', 138 => 'Kannada-Malayalam', 139 => 'Kannada-Oriya', 140 => 'Kannada-Tamil', 141 => 'Kannada-Telugu', 142 => 'Katakana-Hiragana', 143 => 'Katakana-Latin', 144 => 'Katakana-Latin/BGN', 145 => 'Kazakh-Latin/BGN', 146 => 'Kirghiz-Latin/BGN', 147 => 'Knda-Arab', 148 => 'Knda-Beng', 149 => 'Knda-Deva', 150 => 'Knda-Gujr', 151 => 'Knda-Guru', 152 => 'Knda-Latn', 153 => 'Knda-Mlym', 154 => 'Knda-Orya', 155 => 'Knda-Taml', 156 => 'Knda-Telu', 157 => 'Knda-ur', 158 => 'Korean-Latin/BGN', 159 => 'Latin-ASCII', 160 => 'Latin-Arabic', 161 => 'Latin-Armenian', 162 => 'Latin-Bengali', 163 => 'Latin-Bopomofo', 164 => 'Latin-Cyrillic', 165 => 'Latin-Devanagari', 166 => 'Latin-Georgian', 167 => 'Latin-Greek', 168 => 'Latin-Greek/UNGEGN', 169 => 'Latin-Gujarati', 170 => 'Latin-Gurmukhi', 171 => 'Latin-Hangul', 172 => 'Latin-Hebrew', 173 => 'Latin-Hiragana', 174 => 'Latin-Jamo', 175 => 'Latin-Kannada', 176 => 'Latin-Katakana', 177 => 'Latin-Malayalam', 178 => 'Latin-NumericPinyin', 179 => 'Latin-Oriya', 180 => 'Latin-Russian/BGN', 181 => 'Latin-Syriac', 182 => 'Latin-Tamil', 183 => 'Latin-Telugu', 184 => 'Latin-Thaana', 185 => 'Latin-Thai', 186 => 'Latn-Arab', 187 => 'Latn-Armn', 188 => 'Latn-Beng', 189 => 'Latn-Bopo', 190 => 'Latn-Cyrl', 191 => 'Latn-Deva', 192 => 'Latn-Geor', 193 => 'Latn-Grek', 194 => 'Latn-Grek/UNGEGN', 195 => 'Latn-Gujr', 196 => 'Latn-Guru', 197 => 'Latn-Hang', 198 => 'Latn-Hebr', 199 => 'Latn-Hira', 200 => 'Latn-Jamo', 201 => 'Latn-Kana', 202 => 'Latn-Knda', 203 => 'Latn-Mlym', 204 => 'Latn-Orya', 205 => 'Latn-Syrc', 206 => 'Latn-Taml', 207 => 'Latn-Telu', 208 => 'Latn-Thaa', 209 => 'Latn-Thai', 210 => 'Macedonian-Latin/BGN', 211 => 'Malayalam-Arabic', 212 => 'Malayalam-Bengali', 213 => 'Malayalam-Devanagari', 214 => 'Malayalam-Gujarati', 215 => 'Malayalam-Gurmukhi', 216 => 'Malayalam-Kannada', 217 => 'Malayalam-Latin', 218 => 'Malayalam-Oriya', 219 => 'Malayalam-Tamil', 220 => 'Malayalam-Telugu', 221 => 'Maldivian-Latin/BGN', 222 => 'Mlym-Arab', 223 => 'Mlym-Beng', 224 => 'Mlym-Deva', 225 => 'Mlym-Gujr', 226 => 'Mlym-Guru', 227 => 'Mlym-Knda', 228 => 'Mlym-Latn', 229 => 'Mlym-Orya', 230 => 'Mlym-Taml', 231 => 'Mlym-Telu', 232 => 'Mlym-ur', 233 => 'Mongolian-Latin/BGN', 234 => 'NumericPinyin-Latin', 235 => 'NumericPinyin-Pinyin', 236 => 'Oriya-Arabic', 237 => 'Oriya-Bengali', 238 => 'Oriya-Devanagari', 239 => 'Oriya-Gujarati', 240 => 'Oriya-Gurmukhi', 241 => 'Oriya-Kannada', 242 => 'Oriya-Latin', 243 => 'Oriya-Malayalam', 244 => 'Oriya-Tamil', 245 => 'Oriya-Telugu', 246 => 'Orya-Arab', 247 => 'Orya-Beng', 248 => 'Orya-Deva', 249 => 'Orya-Gujr', 250 => 'Orya-Guru', 251 => 'Orya-Knda', 252 => 'Orya-Latn', 253 => 'Orya-Mlym', 254 => 'Orya-Taml', 255 => 'Orya-Telu', 256 => 'Orya-ur', 257 => 'Pashto-Latin/BGN', 258 => 'Persian-Latin/BGN', 259 => 'Pinyin-NumericPinyin', 260 => 'Publishing-Any', 261 => 'Russian-Latin/BGN', 262 => 'Serbian-Latin/BGN', 263 => 'Simplified-Traditional', 264 => 'Syrc-Latn', 265 => 'Syriac-Latin', 266 => 'Tamil-Arabic', 267 => 'Tamil-Bengali', 268 => 'Tamil-Devanagari', 269 => 'Tamil-Gujarati', 270 => 'Tamil-Gurmukhi', 271 => 'Tamil-Kannada', 272 => 'Tamil-Latin', 273 => 'Tamil-Malayalam', 274 => 'Tamil-Oriya', 275 => 'Tamil-Telugu', 276 => 'Taml-Arab', 277 => 'Taml-Beng', 278 => 'Taml-Deva', 279 => 'Taml-Gujr', 280 => 'Taml-Guru', 281 => 'Taml-Knda', 282 => 'Taml-Latn', 283 => 'Taml-Mlym', 284 => 'Taml-Orya', 285 => 'Taml-Telu', 286 => 'Taml-ur', 287 => 'Telu-Arab', 288 => 'Telu-Beng', 289 => 'Telu-Deva', 290 => 'Telu-Gujr', 291 => 'Telu-Guru', 292 => 'Telu-Knda', 293 => 'Telu-Latn', 294 => 'Telu-Mlym', 295 => 'Telu-Orya', 296 => 'Telu-Taml', 297 => 'Telu-ur', 298 => 'Telugu-Arabic', 299 => 'Telugu-Bengali', 300 => 'Telugu-Devanagari', 301 => 'Telugu-Gujarati', 302 => 'Telugu-Gurmukhi', 303 => 'Telugu-Kannada', 304 => 'Telugu-Latin', 305 => 'Telugu-Malayalam', 306 => 'Telugu-Oriya', 307 => 'Telugu-Tamil', 308 => 'Thaa-Latn', 309 => 'Thaana-Latin', 310 => 'Thai-Latin', 311 => 'Thai-Latn', 312 => 'Tone-Digit', 313 => 'Traditional-Simplified', 314 => 'Turkmen-Latin/BGN', 315 => 'Ukrainian-Latin/BGN', 316 => 'Uzbek-Latin/BGN', 317 => 'XSampa-IPA', 318 => 'Zawgyi-my', 319 => 'am-am_FONIPA', 320 => 'am-am_Latn/BGN', 321 => 'am-ar', 322 => 'am-chr', 323 => 'am-fa', 324 => 'am_FONIPA-am', 325 => 'ar-ar_Latn/BGN', 326 => 'az-Lower', 327 => 'az-Title', 328 => 'az-Upper', 329 => 'az_Cyrl-az/BGN', 330 => 'be-be_Latn/BGN', 331 => 'bg-bg_Latn/BGN', 332 => 'blt-blt_FONIPA', 333 => 'ch-am', 334 => 'ch-ar', 335 => 'ch-ch_FONIPA', 336 => 'ch-chr', 337 => 'ch-fa', 338 => 'chr-chr_FONIPA', 339 => 'cs-am', 340 => 'cs-ar', 341 => 'cs-chr', 342 => 'cs-cs_FONIPA', 343 => 'cs-fa', 344 => 'cs-ja', 345 => 'cs-ko', 346 => 'cs_FONIPA-ja', 347 => 'cs_FONIPA-ko', 348 => 'cy-cy_FONIPA', 349 => 'de-ASCII', 350 => 'dsb-dsb_FONIPA', 351 => 'dv-dv_Latn/BGN', 352 => 'el-Lower', 353 => 'el-Title', 354 => 'el-Upper', 355 => 'el-el_Latn/BGN', 356 => 'eo-am', 357 => 'eo-ar', 358 => 'eo-chr', 359 => 'eo-eo_FONIPA', 360 => 'eo-fa', 361 => 'es-am', 362 => 'es-ar', 363 => 'es-chr', 364 => 'es-es_FONIPA', 365 => 'es-fa', 366 => 'es-ja', 367 => 'es-zh', 368 => 'es_419-am', 369 => 'es_419-ar', 370 => 'es_419-chr', 371 => 'es_419-fa', 372 => 'es_419-ja', 373 => 'es_419-zh', 374 => 'es_FONIPA-am', 375 => 'es_FONIPA-es_419_FONIPA', 376 => 'es_FONIPA-ja', 377 => 'es_FONIPA-zh', 378 => 'fa-fa_FONIPA', 379 => 'fa-fa_Latn/BGN', 380 => 'ha-ha_NE', 381 => 'he-he_Latn/BGN', 382 => 'hy-am', 383 => 'hy-ar', 384 => 'hy-chr', 385 => 'hy-fa', 386 => 'hy-hy_FONIPA', 387 => 'hy-hy_Latn/BGN', 388 => 'hy_AREVMDA-am', 389 => 'hy_AREVMDA-ar', 390 => 'hy_AREVMDA-chr', 391 => 'hy_AREVMDA-fa', 392 => 'hy_AREVMDA-hy_AREVMDA_FONIPA', 393 => 'ia-am', 394 => 'ia-ar', 395 => 'ia-chr', 396 => 'ia-fa', 397 => 'ia-ia_FONIPA', 398 => 'it-am', 399 => 'it-ja', 400 => 'ja_Hrkt-ja_Latn/BGN', 401 => 'ja_Latn-ko', 402 => 'ja_Latn-ru', 403 => 'ka-ka_Latn/BGN', 404 => 'ka-ka_Latn/BGN_1981', 405 => 'kk-am', 406 => 'kk-ar', 407 => 'kk-chr', 408 => 'kk-fa', 409 => 'kk-kk_FONIPA', 410 => 'kk-kk_Latn/BGN', 411 => 'ko-ko_Latn/BGN', 412 => 'ky-am', 413 => 'ky-ar', 414 => 'ky-chr', 415 => 'ky-fa', 416 => 'ky-ky_FONIPA', 417 => 'ky-ky_Latn/BGN', 418 => 'la-la_FONIPA', 419 => 'lt-Lower', 420 => 'lt-Title', 421 => 'lt-Upper', 422 => 'mk-mk_Latn/BGN', 423 => 'mn-mn_Latn/BGN', 424 => 'mn-mn_Latn/MNS', 425 => 'my-Zawgyi', 426 => 'my-am', 427 => 'my-ar', 428 => 'my-chr', 429 => 'my-fa', 430 => 'my-my_FONIPA', 431 => 'nl-Title', 432 => 'nv-nv_FONIPA', 433 => 'pl-am', 434 => 'pl-ar', 435 => 'pl-chr', 436 => 'pl-fa', 437 => 'pl-ja', 438 => 'pl-pl_FONIPA', 439 => 'pl_FONIPA-ja', 440 => 'ps-ps_Latn/BGN', 441 => 'rm_SURSILV-am', 442 => 'rm_SURSILV-ar', 443 => 'rm_SURSILV-chr', 444 => 'rm_SURSILV-fa', 445 => 'rm_SURSILV-rm_FONIPA_SURSILV', 446 => 'ro-am', 447 => 'ro-ar', 448 => 'ro-chr', 449 => 'ro-fa', 450 => 'ro-ja', 451 => 'ro-ro_FONIPA', 452 => 'ro_FONIPA-ja', 453 => 'ru-ja', 454 => 'ru-ru_Latn/BGN', 455 => 'ru-zh', 456 => 'ru_Latn-ru/BGN', 457 => 'sat-am', 458 => 'sat-ar', 459 => 'sat-chr', 460 => 'sat-fa', 461 => 'sat_Olck-sat_FONIPA', 462 => 'si-am', 463 => 'si-ar', 464 => 'si-chr', 465 => 'si-fa', 466 => 'si-si_FONIPA', 467 => 'si-si_Latn', 468 => 'sk-am', 469 => 'sk-ar', 470 => 'sk-chr', 471 => 'sk-fa', 472 => 'sk-ja', 473 => 'sk-sk_FONIPA', 474 => 'sk_FONIPA-ja', 475 => 'sr-sr_Latn/BGN', 476 => 'ta-ta_FONIPA', 477 => 'tk_Cyrl-tk/BGN', 478 => 'tlh-am', 479 => 'tlh-ar', 480 => 'tlh-chr', 481 => 'tlh-fa', 482 => 'tlh-tlh_FONIPA', 483 => 'tr-Lower', 484 => 'tr-Title', 485 => 'tr-Upper', 486 => 'ug-ug_FONIPA', 487 => 'uk-uk_Latn/BGN', 488 => 'und_FONIPA-ar', 489 => 'und_FONIPA-chr', 490 => 'und_FONIPA-fa', 491 => 'und_FONIPA-und_FONXSAMP', 492 => 'und_FONXSAMP-und_FONIPA', 493 => 'uz_Cyrl-uz/BGN', 494 => 'uz_Cyrl-uz_Latn', 495 => 'uz_Latn-uz_Cyrl', 496 => 'vec-vec_FONIPA', 497 => 'xh-am', 498 => 'xh-ar', 499 => 'xh-chr', 500 => 'xh-fa', 501 => 'xh-xh_FONIPA', 502 => 'yo-yo_BJ', 503 => 'zh_Latn_PINYIN-ru', 504 => 'zu-am', 505 => 'zu-ar', 506 => 'zu-chr', 507 => 'zu-fa', 508 => 'zu-zu_FONIPA', 509 => 'Any-Null', 510 => 'Any-Lower', 511 => 'Any-Upper', 512 => 'Any-Title', 513 => 'Any-Name', 514 => 'Name-Any', 515 => 'Any-Remove', 516 => 'Any-Hex/Unicode', 517 => 'Any-Hex/Java', 518 => 'Any-Hex/C', 519 => 'Any-Hex/XML', 520 => 'Any-Hex/XML10', 521 => 'Any-Hex/Perl', 522 => 'Any-Hex', 523 => 'Hex-Any/Unicode', 524 => 'Hex-Any/Java', 525 => 'Hex-Any/C', 526 => 'Hex-Any/XML', 527 => 'Hex-Any/XML10', 528 => 'Hex-Any/Perl', 529 => 'Hex-Any', 530 => 'Any-NFC', 531 => 'Any-NFKC', 532 => 'Any-NFD', 533 => 'Any-NFKD', 534 => 'Any-FCD', 535 => 'Any-FCC', 536 => 'Any-fa', 537 => 'Any-ar', 538 => 'Any-chr', 539 => 'Any-rm_FONIPA_SURSILV', 540 => 'Any-am', 541 => 'Any-Latn', 542 => 'Any-dv_Latn/BGN', 543 => 'Any-ro_FONIPA', 544 => 'Any-dsb_FONIPA', 545 => 'Any-Latin', 546 => 'Any-he_Latn/BGN', 547 => 'Any-el_Latn/BGN', 548 => 'Any-eo_FONIPA', 549 => 'Any-si_Latn', 550 => 'Any-si_FONIPA', 551 => 'Any-zh', 552 => 'Any-es_FONIPA', 553 => 'Any-sk_FONIPA', 554 => 'Any-und_FONIPA', 555 => 'Any-und_FONXSAMP', 556 => 'Any-az/BGN', 557 => 'Any-Deva', 558 => 'Any-Arab', 559 => 'Any-Telu', 560 => 'Any-Beng', 561 => 'Any-ur', 562 => 'Any-Orya', 563 => 'Any-Guru', 564 => 'Any-Taml', 565 => 'Any-Gujr', 566 => 'Any-Knda', 567 => 'Any-Armenian', 568 => 'Any-Thaana', 569 => 'Any-Hiragana', 570 => 'Any-Syriac', 571 => 'Any-Jamo', 572 => 'Any-Hangul', 573 => 'Any-Kannada', 574 => 'Any-Georgian', 575 => 'Any-Telugu', 576 => 'Any-Cyrillic', 577 => 'Any-Thai', 578 => 'Any-Oriya', 579 => 'Any-Bopomofo', 580 => 'Any-Malayalam', 581 => 'Any-Gurmukhi', 582 => 'Any-Gujarati', 583 => 'Any-Tamil', 584 => 'Any-Katakana', 585 => 'Any-Hebrew', 586 => 'Any-Bengali', 587 => 'Any-Arabic', 588 => 'Any-Greek', 589 => 'Any-Greek/UNGEGN', 590 => 'Any-Devanagari', 591 => 'Any-fa_FONIPA', 592 => 'Any-fa_Latn/BGN', 593 => 'Any-ta_FONIPA', 594 => 'Any-Mlym', 595 => 'Any-Any', 596 => 'Any-Hant', 597 => 'Any-Hans', 598 => 'Any-uz_Cyrl', 599 => 'Any-blt_FONIPA', 600 => 'Any-ug_FONIPA', 601 => 'Any-uk_Latn/BGN', 602 => 'Any-ha_NE', 603 => 'Any-hy_AREVMDA_FONIPA', 604 => 'Any-hy_FONIPA', 605 => 'Any-hy_Latn/BGN', 606 => 'Any-ia_FONIPA', 607 => 'Any-uz/BGN', 608 => 'Any-uz_Latn', 609 => 'Any-vec_FONIPA', 610 => 'Any-xh_FONIPA', 611 => 'Any-ka_Latn/BGN', 612 => 'Any-ka_Latn/BGN_1981', 613 => 'Any-kk_FONIPA', 614 => 'Any-kk_Latn/BGN', 615 => 'Any-yo_BJ', 616 => 'Any-ky_FONIPA', 617 => 'Any-ky_Latn/BGN', 618 => 'Any-la_FONIPA', 619 => 'Any-ru_Latn/BGN', 620 => 'Any-chr_FONIPA', 621 => 'Any-zu_FONIPA', 622 => 'Any-ru', 623 => 'Any-Geor', 624 => 'Any-Cyrl', 625 => 'Any-Armn', 626 => 'Any-Thaa', 627 => 'Any-Hebr', 628 => 'Any-Grek', 629 => 'Any-Grek/UNGEGN', 630 => 'Any-Syrc', 631 => 'Any-Hira', 632 => 'Any-Hang', 633 => 'Any-Kana', 634 => 'Any-Bopo', 635 => 'Any-mn_Latn/BGN', 636 => 'Any-mn_Latn/MNS', 637 => 'Any-my_FONIPA', 638 => 'Any-sr_Latn/BGN', 639 => 'Any-nv_FONIPA', 640 => 'Any-sat_FONIPA', 641 => 'Any-my', 642 => 'Any-am_FONIPA', 643 => 'Any-am_Latn/BGN', 644 => 'Any-ar_Latn/BGN', 645 => 'Any-mk_Latn/BGN', 646 => 'Any-be_Latn/BGN', 647 => 'Any-bg_Latn/BGN', 648 => 'Any-es_419_FONIPA', 649 => 'Any-pl_FONIPA', 650 => 'Any-ps_Latn/BGN', 651 => 'Any-tk/BGN', 652 => 'Any-ch_FONIPA', 653 => 'Any-cs_FONIPA', 654 => 'Any-cy_FONIPA']; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/utf8_fix.php b/includes/libraries/anti-xss-master/src/voku/helper/data/utf8_fix.php new file mode 100644 index 000000000..e61623f29 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/utf8_fix.php @@ -0,0 +1,152 @@ + '‚', + '„' => '„', + '…' => '…', + '‡' => '‡', + '‰' => '‰', + '‹' => '‹', + '‘' => '‘', + '’' => '’', + '“' => '“', + '•' => '•', + '–' => '–', + '—' => '—', + 'â„¢' => '™', + '›' => '›', + '€' => '€', + // 2 char errors + "\xc2\x80" => "\xe2\x82\xac", // EURO SIGN + "\xc2\x82" => "\xe2\x80\x9a", // SINGLE LOW-9 QUOTATION MARK + "\xc2\x83" => "\xc6\x92", // LATIN SMALL LETTER F WITH HOOK + "\xc2\x84" => "\xe2\x80\x9e", // DOUBLE LOW-9 QUOTATION MARK + "\xc2\x85" => "\xe2\x80\xa6", // HORIZONTAL ELLIPSIS + "\xc2\x86" => "\xe2\x80\xa0", // DAGGER + "\xc2\x87" => "\xe2\x80\xa1", // DOUBLE DAGGER + "\xc2\x88" => "\xcb\x86", // MODIFIER LETTER CIRCUMFLEX ACCENT + "\xc2\x89" => "\xe2\x80\xb0", // PER MILLE SIGN + "\xc2\x8a" => "\xc5\xa0", // LATIN CAPITAL LETTER S WITH CARON + "\xc2\x8b" => "\xe2\x80\xb9", // SINGLE LEFT-POINTING ANGLE QUOTE + "\xc2\x8c" => "\xc5\x92", // LATIN CAPITAL LIGATURE OE + "\xc2\x8e" => "\xc5\xbd", // LATIN CAPITAL LETTER Z WITH CARON + "\xc2\x91" => "\xe2\x80\x98", // LEFT SINGLE QUOTATION MARK + "\xc2\x92" => "\xe2\x80\x99", // RIGHT SINGLE QUOTATION MARK + "\xc2\x93" => "\xe2\x80\x9c", // LEFT DOUBLE QUOTATION MARK + "\xc2\x94" => "\xe2\x80\x9d", // RIGHT DOUBLE QUOTATION MARK + "\xc2\x95" => "\xe2\x80\xa2", // BULLET + "\xc2\x96" => "\xe2\x80\x93", // EN DASH + "\xc2\x97" => "\xe2\x80\x94", // EM DASH + "\xc2\x98" => "\xcb\x9c", // SMALL TILDE + 'Â' => 'Â', + 'Æ’' => 'ƒ', + 'Ã' => 'Ã', + 'Ä' => 'Ä', + 'Ã…' => 'Å', + //'â€' => '†', // duplicate key + 'Æ' => 'Æ', + 'Ç' => 'Ç', + 'ˆ' => 'ˆ', + 'È' => 'È', + 'É' => 'É', + 'Ê' => 'Ê', + 'Ë' => 'Ë', + 'Å’' => 'Œ', + 'ÃŒ' => 'Ì', + 'Ž' => 'Ž', + 'ÃŽ' => 'Î', + 'Ñ' => 'Ñ', + 'Ã’' => 'Ò', + 'Ó' => 'Ó', + 'â€' => '”', + 'Ô' => 'Ô', + 'Õ' => 'Õ', + 'Ö' => 'Ö', + '×' => '×', + 'Ëœ' => '˜', + 'Ø' => 'Ø', + 'Ù' => 'Ù', + 'Å¡' => 'š', + 'Ú' => 'Ú', + 'Û' => 'Û', + 'Å“' => 'œ', + 'Ãœ' => 'Ü', + 'ž' => 'ž', + 'Þ' => 'Þ', + 'Ÿ' => 'Ÿ', + 'ß' => 'ß', + '¡' => '¡', + 'á' => 'á', + '¢' => '¢', + 'â' => 'â', + '£' => '£', + 'ã' => 'ã', + '¤' => '¤', + 'ä' => 'ä', + 'Â¥' => '¥', + 'Ã¥' => 'å', + '¦' => '¦', + 'æ' => 'æ', + '§' => '§', + 'ç' => 'ç', + '¨' => '¨', + 'è' => 'è', + '©' => '©', + 'é' => 'é', + 'ª' => 'ª', + 'ê' => 'ê', + '«' => '«', + 'ë' => 'ë', + '¬' => '¬', + 'ì' => 'ì', + '®' => '®', + 'î' => 'î', + '¯' => '¯', + 'ï' => 'ï', + '°' => '°', + 'ð' => 'ð', + '±' => '±', + 'ñ' => 'ñ', + '²' => '²', + 'ò' => 'ò', + '³' => '³', + 'ó' => 'ó', + '´' => '´', + 'ô' => 'ô', + 'µ' => 'µ', + 'õ' => 'õ', + '¶' => '¶', + 'ö' => 'ö', + '·' => '·', + '÷' => '÷', + '¸' => '¸', + 'ø' => 'ø', + '¹' => '¹', + 'ù' => 'ù', + 'º' => 'º', + 'ú' => 'ú', + '»' => '»', + 'û' => 'û', + '¼' => '¼', + 'ü' => 'ü', + '½' => '½', + 'ý' => 'ý', + '¾' => '¾', + 'þ' => 'þ', + '¿' => '¿', + 'ÿ' => 'ÿ', + 'À' => 'À', + // 1 char errors last (don't use them, because of false-positives) + //'Ã' => 'Á', + //'Å' => 'Š', + //'Ã' => 'Í', + //'Ã' => 'Ï', + //'Ã' => 'Ð', + //'Ã' => 'Ý', + //'Ã' => 'à', + //'Ã' => 'í', +]; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/win1252_to_utf8.php b/includes/libraries/anti-xss-master/src/voku/helper/data/win1252_to_utf8.php new file mode 100644 index 000000000..46ad53c6c --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/win1252_to_utf8.php @@ -0,0 +1,126 @@ + "\xe2\x82\xac", // € + 0x82 => "\xe2\x80\x9a", // ‚ + 0x83 => "\xc6\x92", // ƒ + 0x84 => "\xe2\x80\x9e", // „ + 0x85 => "\xe2\x80\xa6", // … + 0x86 => "\xe2\x80\xa0", // † + 0x87 => "\xe2\x80\xa1", // ‡ + 0x88 => "\xcb\x86", // ˆ + 0x89 => "\xe2\x80\xb0", // ‰ + 0x8a => "\xc5\xa0", // Š + 0x8b => "\xe2\x80\xb9", // ‹ + 0x8c => "\xc5\x92", // Œ + 0x8e => "\xc5\xbd", // Ž + 0x91 => "\xe2\x80\x98", // ‘ + 0x92 => "\xe2\x80\x99", // ’ + 0x93 => "\xe2\x80\x9c", // “ + 0x94 => "\xe2\x80\x9d", // ” + 0x95 => "\xe2\x80\xa2", // • + 0x96 => "\xe2\x80\x93", // – + 0x97 => "\xe2\x80\x94", // — + 0x98 => "\xcb\x9c", // ˜ + 0x99 => "\xe2\x84\xa2", // ™ + 0x9a => "\xc5\xa1", // š + 0x9b => "\xe2\x80\xba", // › + 0x9c => "\xc5\x93", // œ + 0x9e => "\xc5\xbe", // ž + 0x9f => "\xc5\xb8", // Ÿ + 0xa0 => "\xc2\xa0", + 0xa1 => "\xc2\xa1", // ¡ + 0xa2 => "\xc2\xa2", // ¢ + 0xa3 => "\xc2\xa3", // £ + 0xa4 => "\xc2\xa4", // ¤ + 0xa5 => "\xc2\xa5", // ¥ + 0xa6 => "\xc2\xa6", // ¦ + 0xa7 => "\xc2\xa7", // § + 0xa8 => "\xc2\xa8", // ¨ + 0xa9 => "\xc2\xa9", // © + 0xaa => "\xc2\xaa", // ª + 0xab => "\xc2\xab", // « + 0xac => "\xc2\xac", // ¬ + 0xad => "\xc2\xad", // ­ + 0xae => "\xc2\xae", // ® + 0xaf => "\xc2\xaf", // ¯ + 0xb0 => "\xc2\xb0", // ° + 0xb1 => "\xc2\xb1", // ± + 0xb2 => "\xc2\xb2", // ² + 0xb3 => "\xc2\xb3", // ³ + 0xb4 => "\xc2\xb4", // ´ + 0xb5 => "\xc2\xb5", // µ + 0xb6 => "\xc2\xb6", // ¶ + 0xb7 => "\xc2\xb7", // · + 0xb8 => "\xc2\xb8", // ¸ + 0xb9 => "\xc2\xb9", // ¹ + 0xba => "\xc2\xba", // º + 0xbb => "\xc2\xbb", // » + 0xbc => "\xc2\xbc", // ¼ + 0xbd => "\xc2\xbd", // ½ + 0xbe => "\xc2\xbe", // ¾ + 0xbf => "\xc2\xbf", // ¿ + 0xc0 => "\xc3\x80", // À + 0xc1 => "\xc3\x81", // Á + 0xc2 => "\xc3\x82", //  + 0xc3 => "\xc3\x83", // à + 0xc4 => "\xc3\x84", // Ä + 0xc5 => "\xc3\x85", // Å + 0xc6 => "\xc3\x86", // Æ + 0xc7 => "\xc3\x87", // Ç + 0xc8 => "\xc3\x88", // È + 0xc9 => "\xc3\x89", // É + 0xca => "\xc3\x8a", // Ê + 0xcb => "\xc3\x8b", // Ë + 0xcc => "\xc3\x8c", // Ì + 0xcd => "\xc3\x8d", // Í + 0xce => "\xc3\x8e", // Î + 0xcf => "\xc3\x8f", // Ï + 0xd0 => "\xc3\x90", // Ð + 0xd1 => "\xc3\x91", // Ñ + 0xd2 => "\xc3\x92", // Ò + 0xd3 => "\xc3\x93", // Ó + 0xd4 => "\xc3\x94", // Ô + 0xd5 => "\xc3\x95", // Õ + 0xd6 => "\xc3\x96", // Ö + 0xd7 => "\xc3\x97", // × + 0xd8 => "\xc3\x98", // Ø + 0xd9 => "\xc3\x99", // Ù + 0xda => "\xc3\x9a", // Ú + 0xdb => "\xc3\x9b", // Û + 0xdc => "\xc3\x9c", // Ü + 0xdd => "\xc3\x9d", // Ý + 0xde => "\xc3\x9e", // Þ + 0xdf => "\xc3\x9f", // ß + 0xe0 => "\xc3\xa0", // à + 0xe1 => "\xc3\xa1", // á + 0xe2 => "\xc3\xa2", // â + 0xe3 => "\xc3\xa3", // ã + 0xe4 => "\xc3\xa4", // ä + 0xe5 => "\xc3\xa5", // å + 0xe6 => "\xc3\xa6", // æ + 0xe7 => "\xc3\xa7", // ç + 0xe8 => "\xc3\xa8", // è + 0xe9 => "\xc3\xa9", // é + 0xea => "\xc3\xaa", // ê + 0xeb => "\xc3\xab", // ë + 0xec => "\xc3\xac", // ì + 0xed => "\xc3\xad", // í + 0xee => "\xc3\xae", // î + 0xef => "\xc3\xaf", // ï + 0xf0 => "\xc3\xb0", // ð + 0xf1 => "\xc3\xb1", // ñ + 0xf2 => "\xc3\xb2", // ò + 0xf3 => "\xc3\xb3", // ó + 0xf4 => "\xc3\xb4", // ô + 0xf5 => "\xc3\xb5", // õ + 0xf6 => "\xc3\xb6", // ö + 0xf7 => "\xc3\xb7", // ÷ + 0xf8 => "\xc3\xb8", // ø + 0xf9 => "\xc3\xb9", // ù + 0xfa => "\xc3\xba", // ú + 0xfb => "\xc3\xbb", // û + 0xfc => "\xc3\xbc", // ü + 0xfd => "\xc3\xbd", // ý + 0xfe => "\xc3\xbe", // þ +]; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/x000.php b/includes/libraries/anti-xss-master/src/voku/helper/data/x000.php new file mode 100644 index 000000000..6c9d81f9d --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/x000.php @@ -0,0 +1,16 @@ +', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '', 'EUR', // "\xc2\x80" => "\xe2\x82\xac" => EURO SIGN + '', ',', 'f', ',,', // "\xc2\x84" => "\xe2\x80\x9e" => DOUBLE LOW-9 QUOTATION MARK + '...', // "\xc2\x85" => "\xe2\x80\xa6" => HORIZONTAL ELLIPSIS + '+', '++', // "\xc2\x87" => "\xe2\x80\xa1" => DOUBLE DAGGER + '^', '%0', // "\xc2\x89" => "\xe2\x80\xb0" => PER MILLE SIGN + 'S', '<', 'OE', // "\xc2\x8c" => "\xc5\x92" => LATIN CAPITAL LIGATURE OE + '', 'Z', '', '', '\'', // "\xc2\x91" => "\xe2\x80\x98" => LEFT SINGLE QUOTATION MARK + '\'', // "\xc2\x92" => "\xe2\x80\x99" => RIGHT SINGLE QUOTATION MARK + '"', '"', '*', '-', '--', // "\xc2\x97" => "\xe2\x80\x94" => EM DASH + '~', 'tm', 's', '>', 'oe', '', 'z', 'Y', ' ', '!', 'C/', 'PS', '$?', 'Y=', '|', 'SS', '"', '(c)', 'a', '<<', '!', '', '(r)', '-', 'deg', '+-', '2', '3', '\'', 'u', 'P', '*', ',', '1', 'o', '>>', '1/4', '1/2', '3/4', '?', 'A', 'A', 'A', 'A', // Not "AE" - used in languages other than German + 'A', 'A', 'AE', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', // Not "OE" - used in languages other than German + 'O', 'x', 'O', 'U', 'U', 'U', // Not "UE" - used in languages other than German + 'U', 'Y', 'Th', 'ss', 'a', 'a', 'a', 'a', // Not "ae" - used in languages other than German + 'a', 'a', 'ae', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', 'd', 'n', 'o', 'o', 'o', 'o', // Not "oe" - used in languages other than German + 'o', '/', 'o', 'u', 'u', 'u', // Not "ue" - used in languages other than German + 'u', 'y', 'th', 'y', ]; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/x001.php b/includes/libraries/anti-xss-master/src/voku/helper/data/x001.php new file mode 100644 index 000000000..87fb12fb9 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/x001.php @@ -0,0 +1 @@ +', '^', 'V', '^', 'V', '\'', '-', '/', '\\', ',', '_', '\\', '/', ':', '.', '`', '\'', '^', 'V', '+', '-', 'V', '.', '@', ',', '~', '"', 'R', 'X', 'G', 'l', 's', 'x', '?', '', '', '', '', '', '', '', 'V', '=', '"', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]']; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/x003.php b/includes/libraries/anti-xss-master/src/voku/helper/data/x003.php new file mode 100644 index 000000000..3d02b86e2 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/x003.php @@ -0,0 +1 @@ +', '[?]', '[?]', '[?]', 'f', 'v', 'u', 'yr', 'y', 'w', 'th', 'th', 'a', 'o', 'ac', 'ae', 'o', 'o', 'o', 'oe', 'on', 'r', 'k', 'c', 'k', 'g', 'ng', 'g', 'g', 'w', 'h', 'h', 'h', 'h', 'n', 'n', 'n', 'i', 'e', 'j', 'g', 'ae', 'a', 'eo', 'p', 'z', 's', 's', 's', 'c', 'z', 't', 't', 'd', 'b', 'b', 'p', 'p', 'e', 'm', 'm', 'm', 'l', 'l', 'ng', 'ng', 'd', 'o', 'ear', 'ior', 'qu', 'qu', 'qu', 's', 'yr', 'yr', 'yr', 'q', 'x', '.', ':', '+', '17', '18', '19', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]']; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/x017.php b/includes/libraries/anti-xss-master/src/voku/helper/data/x017.php new file mode 100644 index 000000000..8f2a7cac1 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/x017.php @@ -0,0 +1 @@ +', '.', '..', '...', '.', "\n", + "\n\n", + '', '', '', '', '', ' ', '%0', '%00', '\'', '\'\'', '\'\'\'', '`', '``', '```', '^', '<', '>', '*', '!!', '!?', '-', '_', '-', '^', '***', '--', '/', '-[', ']-', '??', '?!', '!?', '7', 'PP', '(]', '[)', '*', '[?]', '[?]', '[?]', '%', '~', '[?]', '[?]', '[?]', "''''", // 0x57 + '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', ' ', '', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '', '', '', '', '', '', '0', 'i', '', '', '4', '5', '6', '7', '8', '9', '+', '-', '=', '(', ')', 'n', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '-', '=', '(', ')', '[?]', 'a', 'e', 'o', 'x', '[?]', 'h', 'k', 'l', 'm', 'n', 'p', 's', 't', '[?]', '[?]', '[?]', 'ECU', 'CL', 'Cr', 'Fr.', 'L.', 'mil', 'N', 'Pts', 'Rs', 'W', 'NS', 'D', 'EUR', 'K', 'T', 'Dr', 'Pf', 'P', 'G', 'A', 'UAH', 'C|', 'L', 'Sm', 'T', 'Rs', 'L', 'M', 'm', 'R', 'l', 'BTC', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '[?]', '', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', ]; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/x021.php b/includes/libraries/anti-xss-master/src/voku/helper/data/x021.php new file mode 100644 index 000000000..1643d67dc --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/x021.php @@ -0,0 +1 @@ +=', '<=', '>=', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]']; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/x023.php b/includes/libraries/anti-xss-master/src/voku/helper/data/x023.php new file mode 100644 index 000000000..b8f4ca0d2 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/x023.php @@ -0,0 +1 @@ + ', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]']; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/x024.php b/includes/libraries/anti-xss-master/src/voku/helper/data/x024.php new file mode 100644 index 000000000..26abcc69a --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/x024.php @@ -0,0 +1 @@ +', '>', '>', '>', '>', '>', 'V', 'V', 'V', 'V', '<', '<', '<', '<', '<', '<', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '#', '#', '#', '#', '#', '^', '^', '^', 'O', '#', '#', '#', '#', 'O', 'O', 'O', 'O', '/', '\\\\', '\\\\', '#', '#', '#', '#', '/']; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/x026.php b/includes/libraries/anti-xss-master/src/voku/helper/data/x026.php new file mode 100644 index 000000000..0c97de3f8 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/x026.php @@ -0,0 +1 @@ + ', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]']; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/x028.php b/includes/libraries/anti-xss-master/src/voku/helper/data/x028.php new file mode 100644 index 000000000..9585d9149 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/x028.php @@ -0,0 +1 @@ +', 'n', 't', 'q', ',', '*', '5', '<', '-', 'u', '8', 'v', '.', '%', '[', '$', '+', 'x', '!', '&', ';', ':', '4', '\\', '0', 'z', '7', '(', '_', '?', 'w', ']', '#', 'y', ')', '=', '[d7]', '[d17]', '[d27]', '[d127]', '[d37]', '[d137]', '[d237]', '[d1237]', '[d47]', '[d147]', '[d247]', '[d1247]', '[d347]', '[d1347]', '[d2347]', '[d12347]', '[d57]', '[d157]', '[d257]', '[d1257]', '[d357]', '[d1357]', '[d2357]', '[d12357]', '[d457]', '[d1457]', '[d2457]', '[d12457]', '[d3457]', '[d13457]', '[d23457]', '[d123457]', '[d67]', '[d167]', '[d267]', '[d1267]', '[d367]', '[d1367]', '[d2367]', '[d12367]', '[d467]', '[d1467]', '[d2467]', '[d12467]', '[d3467]', '[d13467]', '[d23467]', '[d123467]', '[d567]', '[d1567]', '[d2567]', '[d12567]', '[d3567]', '[d13567]', '[d23567]', '[d123567]', '[d4567]', '[d14567]', '[d24567]', '[d124567]', '[d34567]', '[d134567]', '[d234567]', '[d1234567]', '[d8]', '[d18]', '[d28]', '[d128]', '[d38]', '[d138]', '[d238]', '[d1238]', '[d48]', '[d148]', '[d248]', '[d1248]', '[d348]', '[d1348]', '[d2348]', '[d12348]', '[d58]', '[d158]', '[d258]', '[d1258]', '[d358]', '[d1358]', '[d2358]', '[d12358]', '[d458]', '[d1458]', '[d2458]', '[d12458]', '[d3458]', '[d13458]', '[d23458]', '[d123458]', '[d68]', '[d168]', '[d268]', '[d1268]', '[d368]', '[d1368]', '[d2368]', '[d12368]', '[d468]', '[d1468]', '[d2468]', '[d12468]', '[d3468]', '[d13468]', '[d23468]', '[d123468]', '[d568]', '[d1568]', '[d2568]', '[d12568]', '[d3568]', '[d13568]', '[d23568]', '[d123568]', '[d4568]', '[d14568]', '[d24568]', '[d124568]', '[d34568]', '[d134568]', '[d234568]', '[d1234568]', '[d78]', '[d178]', '[d278]', '[d1278]', '[d378]', '[d1378]', '[d2378]', '[d12378]', '[d478]', '[d1478]', '[d2478]', '[d12478]', '[d3478]', '[d13478]', '[d23478]', '[d123478]', '[d578]', '[d1578]', '[d2578]', '[d12578]', '[d3578]', '[d13578]', '[d23578]', '[d123578]', '[d4578]', '[d14578]', '[d24578]', '[d124578]', '[d34578]', '[d134578]', '[d234578]', '[d1234578]', '[d678]', '[d1678]', '[d2678]', '[d12678]', '[d3678]', '[d13678]', '[d23678]', '[d123678]', '[d4678]', '[d14678]', '[d24678]', '[d124678]', '[d34678]', '[d134678]', '[d234678]', '[d1234678]', '[d5678]', '[d15678]', '[d25678]', '[d125678]', '[d35678]', '[d135678]', '[d235678]', '[d1235678]', '[d45678]', '[d145678]', '[d245678]', '[d1245678]', '[d345678]', '[d1345678]', '[d2345678]', '[d12345678]']; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/x029.php b/includes/libraries/anti-xss-master/src/voku/helper/data/x029.php new file mode 100644 index 000000000..5162de386 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/x029.php @@ -0,0 +1 @@ +', '%', '[?]', '[?]', '>', '=', '[?]', '/', '-', '~', '\\', '/', '~', '~', '|-', '-|', '[?]', '[?]', '[?]', '[?]', '<=', '=>', '((', '))', '[?]', '[?]', '::', '[?]', '?', '\'', 'o', '.', ',', '.', ',', ';', '[?]', '[?]', '[?]', '[?]', '----', '------', 'x', '|', '[?]', '[?]', '=', ',', '"', '`--', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?]', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?] ', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]']; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/x02f.php b/includes/libraries/anti-xss-master/src/voku/helper/data/x02f.php new file mode 100644 index 000000000..5147b5740 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/x02f.php @@ -0,0 +1 @@ + ', '<<', '>> ', '[', '] ', '{', '} ', '[(', ')] ', '@', 'X ', '[', '] ', '[[', ']] ', '((', ')) ', '[[', ']] ', '~ ', '``', '\'\'', ',,', '@', '1', '2', '3', '4', '5', '6', '7', '8', '9', '', '', '', '', '', '', '~', '+', '+', '+', '+', '', '@', ' // ', '+10+', '+20+', '+30+', '[?]', '[?]', '[?]', '', '', '[?]', 'a', 'a', 'i', 'i', 'u', 'u', 'e', 'e', 'o', 'o', 'ka', 'ga', 'ki', 'gi', 'ku', 'gu', 'ke', 'ge', 'ko', 'go', 'sa', 'za', 'shi', // 0x57 + 'zi', 'su', 'zu', 'se', 'ze', 'so', 'zo', 'ta', 'da', 'chi', // 0x61 + 'di', 'tsu', // 0x63 + 'tsu', // 0x64 + 'du', 'te', 'de', 'to', 'do', 'na', 'ni', 'nu', 'ne', 'no', 'ha', 'ba', 'pa', 'hi', 'bi', 'pi', 'hu', 'bu', 'pu', 'he', 'be', 'pe', 'ho', 'bo', 'po', 'ma', 'mi', 'mu', 'me', 'mo', 'ya', 'ya', 'yu', 'yu', 'yo', 'yo', 'ra', 'ri', 'ru', 're', 'ro', 'wa', 'wa', 'wi', 'we', 'wo', 'n', 'vu', '[?]', '[?]', '[?]', '[?]', '', '', '', '', '"', '"', '[?]', '[?]', 'a', 'a', 'i', 'i', 'u', 'u', 'e', 'e', 'o', 'o', 'ka', 'ga', 'ki', 'gi', 'ku', 'gu', 'ke', 'ge', 'ko', 'go', 'sa', 'za', 'shi', // 0xb7 + 'zi', 'su', 'zu', 'se', 'ze', 'so', 'zo', 'ta', 'da', 'chi', // 0xc1 + 'di', 'tsu', // 0xc3 + 'tsu', // 0xc4 + 'du', 'te', 'de', 'to', 'do', 'na', 'ni', 'nu', 'ne', 'no', 'ha', 'ba', 'pa', 'hi', 'bi', 'pi', 'hu', 'bu', 'pu', 'he', 'be', 'pe', 'ho', 'bo', 'po', 'ma', 'mi', 'mu', 'me', 'mo', 'ya', 'ya', 'yu', 'yu', 'yo', 'yo', 'ra', 'ri', 'ru', 're', 'ro', 'wa', 'wa', 'wi', 'we', 'wo', 'n', 'vu', 'ka', 'ke', 'va', 'vi', 've', 'vo', '', '', '"', '"', ]; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/x031.php b/includes/libraries/anti-xss-master/src/voku/helper/data/x031.php new file mode 100644 index 000000000..72c0260cf --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/x031.php @@ -0,0 +1 @@ +>', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '(g)', '(n)', '(d)', '(r)', '(m)', '(b)', '(s)', '()', '(j)', '(c)', '(k)', '(t)', '(p)', '(h)', '(ga)', '(na)', '(da)', '(ra)', '(ma)', '(ba)', '(sa)', '(a)', '(ja)', '(ca)', '(ka)', '(ta)', '(pa)', '(ha)', '[?]', '[?]', '[?]', 'KIS ', '(1) ', '(2) ', '(3) ', '(4) ', '(5) ', '(6) ', '(7) ', '(8) ', '(9) ', '(10) ', '(Yue) ', '(Huo) ', '(Shui) ', '(Mu) ', '(Jin) ', '(Tu) ', '(Ri) ', '(Zhu) ', '(You) ', '(She) ', '(Ming) ', '(Te) ', '(Cai) ', '(Zhu) ', '(Lao) ', '(Mi) ', '(Nan) ', '(Nu) ', '(Shi) ', '(You) ', '(Yin) ', '(Zhu) ', '(Xiang) ', '(Xiu) ', '(Xie) ', '(Zheng) ', '(Shang) ', '(Zhong) ', '(Xia) ', '(Zuo) ', '(You) ', '(Yi) ', '(Zong) ', '(Xue) ', '(Jian) ', '(Qi) ', '(Zi) ', '(Xie) ', '(Ye) ', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '1M', '2M', '3M', '4M', '5M', '6M', '7M', '8M', '9M', '10M', '11M', '12M', 'Hg', 'erg', 'eV', 'LTD', 'a', 'i', 'u', 'u', 'o', 'ka', 'ki', 'ku', 'ke', 'ko', 'sa', 'si', 'su', 'se', 'so', 'ta', 'ti', 'tu', 'te', 'to', 'na', 'ni', 'nu', 'ne', 'no', 'ha', 'hi', 'hu', 'he', 'ho', 'ma', 'mi', 'mu', 'me', 'mo', 'ya', 'yu', 'yo', 'ra', 'ri', 'ru', 're', 'ro', 'wa', 'wi', 'we', 'wo']; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/x033.php b/includes/libraries/anti-xss-master/src/voku/helper/data/x033.php new file mode 100644 index 000000000..8505337e3 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/x033.php @@ -0,0 +1 @@ +> ', '<', '> ', '[', '] ', '{', '}', '[?]', '[?]', '[?]', '[?]', '', '', '', '', '', '', '', ',', ',', '.', '', ';', ':', '?', '!', '-', '(', ')', '{', '}', '{', '}', '#', '&', '*', '+', '-', '<', '>', '=', '', '\\', '$', '%', '@', '[?]', '[?]', '[?]', '[?]', '', '', '', '[?]', '', '[?]', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '[?]', '[?]', '']; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/x0ff.php b/includes/libraries/anti-xss-master/src/voku/helper/data/x0ff.php new file mode 100644 index 000000000..b3a15398c --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/x0ff.php @@ -0,0 +1 @@ +', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '[?]', '[?]', '.', '[', ']', ',', '*', 'wo', 'a', 'i', 'u', 'e', 'o', 'ya', 'yu', 'yo', 'tu', '+', 'a', 'i', 'u', 'e', 'o', 'ka', 'ki', 'ku', 'ke', 'ko', 'sa', 'si', 'su', 'se', 'so', 'ta', 'ti', 'tu', 'te', 'to', 'na', 'ni', 'nu', 'ne', 'no', 'ha', 'hi', 'hu', 'he', 'ho', 'ma', 'mi', 'mu', 'me', 'mo', 'ya', 'yu', 'yo', 'ra', 'ri', 'ru', 're', 'ro', 'wa', 'n', ':', ';', '', 'g', 'gg', 'gs', 'n', 'nj', 'nh', 'd', 'dd', 'r', 'lg', 'lm', 'lb', 'ls', 'lt', 'lp', 'rh', 'm', 'b', 'bb', 'bs', 's', 'ss', '', 'j', 'jj', 'c', 'k', 't', 'p', 'h', '[?]', '[?]', '[?]', 'a', 'ae', 'ya', 'yae', 'eo', 'e', '[?]', '[?]', 'yeo', 'ye', 'o', 'wa', 'wae', 'oe', '[?]', '[?]', 'yo', 'u', 'weo', 'we', 'wi', 'yu', '[?]', '[?]', 'eu', 'yi', 'i', '[?]', '[?]', '[?]', '/C', 'PS', '!', '-', '|', 'Y=', 'W=', '[?]', '|', '-', '|', '-', '|', '#', 'O', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '{', '|', '}', '', '', '', '']; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/x1d4.php b/includes/libraries/anti-xss-master/src/voku/helper/data/x1d4.php new file mode 100644 index 000000000..ad8d3b257 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/x1d4.php @@ -0,0 +1 @@ + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 52 => 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 78 => 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 104 => 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 130 => 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 156 => 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 181 => 'Z', 182 => 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 208 => 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 234 => 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/x1d5.php b/includes/libraries/anti-xss-master/src/voku/helper/data/x1d5.php new file mode 100644 index 000000000..a2a9b908d --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/x1d5.php @@ -0,0 +1,4 @@ + 'w', 'x', 'y', 'z', 4 => 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 30 => 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 56 => 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 82 => 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 108 => 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 134 => 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 160 => 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 186 => 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 212 => 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 238 => 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ]; diff --git a/includes/libraries/anti-xss-master/src/voku/helper/data/x1d6.php b/includes/libraries/anti-xss-master/src/voku/helper/data/x1d6.php new file mode 100644 index 000000000..315ef5e40 --- /dev/null +++ b/includes/libraries/anti-xss-master/src/voku/helper/data/x1d6.php @@ -0,0 +1 @@ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 80 => 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 112 => 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 230 => 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ]; diff --git a/sources/core.php b/sources/core.php index 16e8d5b69..59d7b12ee 100755 --- a/sources/core.php +++ b/sources/core.php @@ -48,6 +48,8 @@ function redirect($url) { // Load AntiXSS + include_once '../includes/libraries/anti-xss-master/src/voku/helper/ASCII.php'; + include_once '../includes/libraries/anti-xss-master/src/voku/helper/UTF8.php'; include_once '../includes/libraries/anti-xss-master/src/voku/helper/AntiXSS.php'; $antiXss = new voku\helper\AntiXSS(); if (! headers_sent()) { //If headers not sent yet... then do php redirect diff --git a/sources/identify.php b/sources/identify.php index e94d1fbe4..3ab6ba711 100755 --- a/sources/identify.php +++ b/sources/identify.php @@ -188,6 +188,8 @@ function identifyUser(string $sentData, array $SETTINGS): bool error_reporting(E_ERROR); // Load AntiXSS + include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/anti-xss-master/src/voku/helper/ASCII.php'; + include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/anti-xss-master/src/voku/helper/UTF8.php'; include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/anti-xss-master/src/voku/helper/AntiXSS.php'; $antiXss = new voku\helper\AntiXSS(); diff --git a/sources/items.queries.php b/sources/items.queries.php index 670c17f24..1a902ed98 100755 --- a/sources/items.queries.php +++ b/sources/items.queries.php @@ -78,6 +78,12 @@ ); } +// Load AntiXSS +include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/anti-xss-master/src/voku/helper/ASCII.php'; +include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/anti-xss-master/src/voku/helper/UTF8.php'; +include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/anti-xss-master/src/voku/helper/AntiXSS.php'; +$antiXss = new voku\helper\AntiXSS(); + // Connect to mysql server require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php'; if (defined('DB_PASSWD_CLEAR') === false) { @@ -195,7 +201,7 @@ // Prepare variables $post_anyone_can_modify = filter_var($dataReceived['anyone_can_modify'], FILTER_SANITIZE_NUMBER_INT); $post_complexity_level = filter_var($dataReceived['complexity_level'], FILTER_SANITIZE_NUMBER_INT); - $post_description = ($dataReceived['description']); + $post_description = $antiXss->xss_clean($dataReceived['description']); $post_diffusion_list = filter_var( $dataReceived['diffusion_list'], FILTER_SANITIZE_STRING @@ -898,7 +904,7 @@ $dataReceived['fields'], FILTER_SANITIZE_STRING )); - $post_description = ($dataReceived['description']); + $post_description = $antiXss->xss_clean($dataReceived['description']); $post_fa_icon = filter_var(($dataReceived['fa_icon']), FILTER_SANITIZE_STRING); //-> DO A SET OF CHECKS