utf8.php
1 <?php
2
/**
3  * Provides string functions for UTF-8 strings
4  * use Iñtërnâtiônàlizætiøn for testing (it contains 20 characters)
5  * 
6  * This class is implemented to provide a UTF-8 version of almost every built-in
7  * PHP string function. For more information about UTF-8, please visit
8  * http://flourishlib.com/docs/UTF-8.
9  * 
10  * @author Zhou Yuan <yuanzhou19@gmail.com>
11  * @link http://www.infopotato.com/
12  * @copyright Copyright &copy; 2009-2011 Zhou Yuan
13  * @license http://www.opensource.org/licenses/mit-license.php MIT Licence
14  * @link    based on   http://flourishlib.com/fUTF8
15  */
16
class UTF8 {
17     
/**
18      * Depending how things are compiled, NetBSD and Solaris don't support //IGNORE in iconv()
19      * 
20      * If //IGNORE support is not provided strings with invalid characters will be truncated
21      * 
22      * @var boolean
23      */
24     
private static $_can_ignore_invalid NULL;
25     
26     
/**
27      * All lowercase UTF-8 characters mapped to uppercase characters
28      * 
29      * @var array
30      */
31     
private static $_lower_to_upper = array(
32         
'a' => 'A''b' => 'B''c' => 'C''d' => 'D''e' => 'E''f' => 'F',
33         
'g' => 'G''h' => 'H''i' => 'I''j' => 'J''k' => 'K''l' => 'L',
34         
'm' => 'M''n' => 'N''o' => 'O''p' => 'P''q' => 'Q''r' => 'R',
35         
's' => 'S''t' => 'T''u' => 'U''v' => 'V''w' => 'W''x' => 'X',
36         
'y' => 'Y''z' => 'Z''à' => 'À''á' => 'Á''â' => 'Â''ã' => 'Ã',
37         
'ä' => 'Ä''å' => 'Å''æ' => 'Æ''ç' => 'Ç''è' => 'È''é' => 'É',
38         
'ê' => 'Ê''ë' => 'Ë''ì' => 'Ì''í' => 'Í''î' => 'Î''ï' => 'Ï',
39         
'ð' => 'Ð''ñ' => 'Ñ''ò' => 'Ò''ó' => 'Ó''ô' => 'Ô''õ' => 'Õ',
40         
'ö' => 'Ö''ø' => 'Ø''ù' => 'Ù''ú' => 'Ú''û' => 'Û''ü' => 'Ü',
41         
'ý' => 'Ý''þ' => 'Þ''ā' => 'Ā''ă' => 'Ă''ą' => 'Ą''ć' => 'Ć',
42         
'ĉ' => 'Ĉ''ċ' => 'Ċ''č' => 'Č''ď' => 'Ď''đ' => 'Đ''ē' => 'Ē',
43         
'ĕ' => 'Ĕ''ė' => 'Ė''ę' => 'Ę''ě' => 'Ě''ĝ' => 'Ĝ''ğ' => 'Ğ',
44         
'ġ' => 'Ġ''ģ' => 'Ģ''ĥ' => 'Ĥ''ħ' => 'Ħ''ĩ' => 'Ĩ''ī' => 'Ī',
45         
'ĭ' => 'Ĭ''į' => 'Į''ij' => 'IJ''ĵ' => 'Ĵ''ķ' => 'Ķ''ĺ' => 'Ĺ',
46         
'ļ' => 'Ļ''ľ' => 'Ľ''ŀ' => 'Ŀ''ł' => 'Ł''ń' => 'Ń''ņ' => 'Ņ',
47         
'ň' => 'Ň''ŋ' => 'Ŋ''ō' => 'Ō''ŏ' => 'Ŏ''ő' => 'Ő''œ' => 'Œ',
48         
'ŕ' => 'Ŕ''ŗ' => 'Ŗ''ř' => 'Ř''ś' => 'Ś''ŝ' => 'Ŝ''ş' => 'Ş',
49         
'š' => 'Š''ţ' => 'Ţ''ť' => 'Ť''ŧ' => 'Ŧ''ũ' => 'Ũ''ū' => 'Ū',
50         
'ŭ' => 'Ŭ''ů' => 'Ů''ű' => 'Ű''ų' => 'Ų''ŵ' => 'Ŵ''ŷ' => 'Ŷ',
51         
'ÿ' => 'Ÿ''ź' => 'Ź''ż' => 'Ż''ž' => 'Ž''ɓ' => 'Ɓ''ƃ' => 'Ƃ',
52         
'ƅ' => 'Ƅ''ɔ' => 'Ɔ''ƈ' => 'Ƈ''ɗ' => 'Ɗ''ƌ' => 'Ƌ''ɘ' => 'Ǝ',
53         
'ə' => 'Ə''ɛ' => 'Ɛ''ƒ' => 'Ƒ''ɠ' => 'Ɠ''ɣ' => 'Ɣ''ɩ' => 'Ɩ',
54         
'ɨ' => 'Ɨ''ƙ' => 'Ƙ''ɯ' => 'Ɯ''ɲ' => 'Ɲ''ɵ' => 'Ɵ''ơ' => 'Ơ',
55         
'ƣ' => 'Ƣ''ƥ' => 'Ƥ''ƨ' => 'Ƨ''ʃ' => 'Ʃ''ƭ' => 'Ƭ''ʈ' => 'Ʈ',
56         
'ư' => 'Ư''ʊ' => 'Ʊ''ʋ' => 'Ʋ''ƴ' => 'Ƴ''ƶ' => 'Ƶ''ʒ' => 'Ʒ',
57         
'ƹ' => 'Ƹ''ƽ' => 'Ƽ''dž' => 'DŽ''dž' => 'Dž''lj' => 'LJ''lj' => 'Lj',
58         
'nj' => 'NJ''nj' => 'Nj''ǎ' => 'Ǎ''ǐ' => 'Ǐ''ǒ' => 'Ǒ''ǔ' => 'Ǔ',
59         
'ǖ' => 'Ǖ''ǘ' => 'Ǘ''ǚ' => 'Ǚ''ǜ' => 'Ǜ''ǟ' => 'Ǟ''ǡ' => 'Ǡ',
60         
'ǣ' => 'Ǣ''ǥ' => 'Ǥ''ǧ' => 'Ǧ''ǩ' => 'Ǩ''ǫ' => 'Ǫ''ǭ' => 'Ǭ',
61         
'ǯ' => 'Ǯ''dz' => 'DZ''ǵ' => 'Ǵ''ǻ' => 'Ǻ''ǽ' => 'Ǽ''ǿ' => 'Ǿ',
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         
'ճ' => 'Ճ''մ' => 'Մ''յ' => 'Յ''ն' => 'Ն''շ' => 'Շ''ո' => 'Ո',
93         
'չ' => 'Չ''պ' => 'Պ''ջ' => 'Ջ''ռ' => 'Ռ''ս' => 'Ս''վ' => 'Վ',
94         
'տ' => 'Տ''ր' => 'Ր''ց' => 'Ց''ւ' => 'Ւ''փ' => 'Փ''ք' => 'Ք',
95         
'օ' => 'Օ''ֆ' => 'Ֆ''ა' => 'Ⴀ''ბ' => 'Ⴁ''გ' => 'Ⴂ''დ' => 'Ⴃ',
96         
'ე' => 'Ⴄ''ვ' => 'Ⴅ''ზ' => 'Ⴆ''თ' => 'Ⴇ''ი' => 'Ⴈ''კ' => 'Ⴉ',
97         
'ლ' => 'Ⴊ''მ' => 'Ⴋ''ნ' => 'Ⴌ''ო' => 'Ⴍ''პ' => 'Ⴎ''ჟ' => 'Ⴏ',
98         
'რ' => 'Ⴐ''ს' => 'Ⴑ''ტ' => 'Ⴒ''უ' => 'Ⴓ''ფ' => 'Ⴔ''ქ' => 'Ⴕ',
99         
'ღ' => 'Ⴖ''ყ' => 'Ⴗ''შ' => 'Ⴘ''ჩ' => 'Ⴙ''ც' => 'Ⴚ''ძ' => 'Ⴛ',
100         
'წ' => 'Ⴜ''ჭ' => 'Ⴝ''ხ' => 'Ⴞ''ჯ' => 'Ⴟ''ჰ' => 'Ⴠ''ჱ' => 'Ⴡ',
101         
'ჲ' => 'Ⴢ''ჳ' => 'Ⴣ''ჴ' => 'Ⴤ''ჵ' => 'Ⴥ''ḁ' => 'Ḁ''ḃ' => 'Ḃ',
102         
'ḅ' => 'Ḅ''ḇ' => 'Ḇ''ḉ' => 'Ḉ''ḋ' => 'Ḋ''ḍ' => 'Ḍ''ḏ' => 'Ḏ',
103         
'ḑ' => 'Ḑ''ḓ' => 'Ḓ''ḕ' => 'Ḕ''ḗ' => 'Ḗ''ḙ' => 'Ḙ''ḛ' => 'Ḛ',
104         
'ḝ' => 'Ḝ''ḟ' => 'Ḟ''ḡ' => 'Ḡ''ḣ' => 'Ḣ''ḥ' => 'Ḥ''ḧ' => 'Ḧ',
105         
'ḩ' => 'Ḩ''ḫ' => 'Ḫ''ḭ' => 'Ḭ''ḯ' => 'Ḯ''ḱ' => 'Ḱ''ḳ' => 'Ḳ',
106         
'ḵ' => 'Ḵ''ḷ' => 'Ḷ''ḹ' => 'Ḹ''ḻ' => 'Ḻ''ḽ' => 'Ḽ''ḿ' => 'Ḿ',
107         
'ṁ' => 'Ṁ''ṃ' => 'Ṃ''ṅ' => 'Ṅ''ṇ' => 'Ṇ''ṉ' => 'Ṉ''ṋ' => 'Ṋ',
108         
'ṍ' => 'Ṍ''ṏ' => 'Ṏ''ṑ' => 'Ṑ''ṓ' => 'Ṓ''ṕ' => 'Ṕ''ṗ' => 'Ṗ',
109         
'ṙ' => 'Ṙ''ṛ' => 'Ṛ''ṝ' => 'Ṝ''ṟ' => 'Ṟ''ṡ' => 'Ṡ''ṣ' => 'Ṣ',
110         
'ṥ' => 'Ṥ''ṧ' => 'Ṧ''ṩ' => 'Ṩ''ṫ' => 'Ṫ''ṭ' => 'Ṭ''ṯ' => 'Ṯ',
111         
'ṱ' => 'Ṱ''ṳ' => 'Ṳ''ṵ' => 'Ṵ''ṷ' => 'Ṷ''ṹ' => 'Ṹ''ṻ' => 'Ṻ',
112         
'ṽ' => 'Ṽ''ṿ' => 'Ṿ''ẁ' => 'Ẁ''ẃ' => 'Ẃ''ẅ' => 'Ẅ''ẇ' => 'Ẇ',
113         
'ẉ' => 'Ẉ''ẋ' => 'Ẋ''ẍ' => 'Ẍ''ẏ' => 'Ẏ''ẑ' => 'Ẑ''ẓ' => 'Ẓ',
114         
'ẕ' => 'Ẕ''ạ' => 'Ạ''ả' => 'Ả''ấ' => 'Ấ''ầ' => 'Ầ''ẩ' => 'Ẩ',
115         
'ẫ' => 'Ẫ''ậ' => 'Ậ''ắ' => 'Ắ''ằ' => 'Ằ''ẳ' => 'Ẳ''ẵ' => 'Ẵ',
116         
'ặ' => 'Ặ''ẹ' => 'Ẹ''ẻ' => 'Ẻ''ẽ' => 'Ẽ''ế' => 'Ế''ề' => 'Ề',
117         
'ể' => 'Ể''ễ' => 'Ễ''ệ' => 'Ệ''ỉ' => 'Ỉ''ị' => 'Ị''ọ' => 'Ọ',
118         
'ỏ' => 'Ỏ''ố' => 'Ố''ồ' => 'Ồ''ổ' => 'Ổ''ỗ' => 'Ỗ''ộ' => 'Ộ',
119         
'ớ' => 'Ớ''ờ' => 'Ờ''ở' => 'Ở''ỡ' => 'Ỡ''ợ' => 'Ợ''ụ' => 'Ụ',
120         
'ủ' => 'Ủ''ứ' => 'Ứ''ừ' => 'Ừ''ử' => 'Ử''ữ' => 'Ữ''ự' => 'Ự',
121         
'ỳ' => 'Ỳ''ỵ' => 'Ỵ''ỷ' => 'Ỷ''ỹ' => 'Ỹ''ἀ' => 'Ἀ''ἁ' => 'Ἁ',
122         
'ἂ' => 'Ἂ''ἃ' => 'Ἃ''ἄ' => 'Ἄ''ἅ' => 'Ἅ''ἆ' => 'Ἆ''ἇ' => 'Ἇ',
123         
'ἐ' => 'Ἐ''ἑ' => 'Ἑ''ἒ' => 'Ἒ''ἓ' => 'Ἓ''ἔ' => 'Ἔ''ἕ' => 'Ἕ',
124         
'ἠ' => 'Ἠ''ἡ' => 'Ἡ''ἢ' => 'Ἢ''ἣ' => 'Ἣ''ἤ' => 'Ἤ''ἥ' => 'Ἥ',
125         
'ἦ' => 'Ἦ''ἧ' => 'Ἧ''ἰ' => 'Ἰ''ἱ' => 'Ἱ''ἲ' => 'Ἲ''ἳ' => 'Ἳ',
126         
'ἴ' => 'Ἴ''ἵ' => 'Ἵ''ἶ' => 'Ἶ''ἷ' => 'Ἷ''ὀ' => 'Ὀ''ὁ' => 'Ὁ',
127         
'ὂ' => 'Ὂ''ὃ' => 'Ὃ''ὄ' => 'Ὄ''ὅ' => 'Ὅ''ὑ' => 'Ὑ''ὓ' => 'Ὓ',
128         
'ὕ' => 'Ὕ''ὗ' => 'Ὗ''ὠ' => 'Ὠ''ὡ' => 'Ὡ''ὢ' => 'Ὢ''ὣ' => 'Ὣ',
129         
'ὤ' => 'Ὤ''ὥ' => 'Ὥ''ὦ' => 'Ὦ''ὧ' => 'Ὧ''ᾀ' => 'ᾈ''ᾁ' => 'ᾉ',
130         
'ᾂ' => 'ᾊ''ᾃ' => 'ᾋ''ᾄ' => 'ᾌ''ᾅ' => 'ᾍ''ᾆ' => 'ᾎ''ᾇ' => 'ᾏ',
131         
'ᾐ' => 'ᾘ''ᾑ' => 'ᾙ''ᾒ' => 'ᾚ''ᾓ' => 'ᾛ''ᾔ' => 'ᾜ''ᾕ' => 'ᾝ',
132         
'ᾖ' => 'ᾞ''ᾗ' => 'ᾟ''ᾠ' => 'ᾨ''ᾡ' => 'ᾩ''ᾢ' => 'ᾪ''ᾣ' => 'ᾫ',
133         
'ᾤ' => 'ᾬ''ᾥ' => 'ᾭ''ᾦ' => 'ᾮ''ᾧ' => 'ᾯ''ᾰ' => 'Ᾰ''ᾱ' => 'Ᾱ',
134         
'ῐ' => 'Ῐ''ῑ' => 'Ῑ''ῠ' => 'Ῠ''ῡ' => 'Ῡ''ⓐ' => 'Ⓐ''ⓑ' => 'Ⓑ',
135         
'ⓒ' => 'Ⓒ''ⓓ' => 'Ⓓ''ⓔ' => 'Ⓔ''ⓕ' => 'Ⓕ''ⓖ' => 'Ⓖ''ⓗ' => 'Ⓗ',
136         
'ⓘ' => 'Ⓘ''ⓙ' => 'Ⓙ''ⓚ' => 'Ⓚ''ⓛ' => 'Ⓛ''ⓜ' => 'Ⓜ''ⓝ' => 'Ⓝ',
137         
'ⓞ' => 'Ⓞ''ⓟ' => 'Ⓟ''ⓠ' => 'Ⓠ''ⓡ' => 'Ⓡ''ⓢ' => 'Ⓢ''ⓣ' => 'Ⓣ',
138         
'ⓤ' => 'Ⓤ''ⓥ' => 'Ⓥ''ⓦ' => 'Ⓦ''ⓧ' => 'Ⓧ''ⓨ' => 'Ⓨ''ⓩ' => 'Ⓩ',
139         
'a' => 'A''b' => 'B''c' => 'C''d' => 'D''e' => 'E''f' => 'F',
140         
'g' => 'G''h' => 'H''i' => 'I''j' => 'J''k' => 'K''l' => 'L',
141         
'm' => 'M''n' => 'N''o' => 'O''p' => 'P''q' => 'Q''r' => 'R',
142         
's' => 'S''t' => 'T''u' => 'U''v' => 'V''w' => 'W''x' => 'X',
143         
'y' => 'Y''z' => 'Z'
144     
);
145     
146     
/**
147      * All lowercase UTF-8 characters not properly handled by [http://php.net/mb_strtoupper mb_strtoupper()] mapped to uppercase characters
148      * 
149      * @var array
150      */
151     
private static $_mb_lower_to_upper_fix = array(
152         
'ɘ' => 'Ǝ''Dz' => 'DZ''ა' => 'Ⴀ''ბ' => 'Ⴁ''გ' => 'Ⴂ''დ' => 'Ⴃ',
153         
'ე' => 'Ⴄ''ვ' => 'Ⴅ''ზ' => 'Ⴆ''თ' => 'Ⴇ''ი' => 'Ⴈ''კ' => 'Ⴉ',
154         
'ლ' => 'Ⴊ''მ' => 'Ⴋ''ნ' => 'Ⴌ''ო' => 'Ⴍ''პ' => 'Ⴎ''ჟ' => 'Ⴏ',
155         
'რ' => 'Ⴐ''ს' => 'Ⴑ''ტ' => 'Ⴒ''უ' => 'Ⴓ''ფ' => 'Ⴔ''ქ' => 'Ⴕ',
156         
'ღ' => 'Ⴖ''ყ' => 'Ⴗ''შ' => 'Ⴘ''ჩ' => 'Ⴙ''ც' => 'Ⴚ''ძ' => 'Ⴛ',
157         
'წ' => 'Ⴜ''ჭ' => 'Ⴝ''ხ' => 'Ⴞ''ჯ' => 'Ⴟ''ჰ' => 'Ⴠ''ჱ' => 'Ⴡ',
158         
'ჲ' => 'Ⴢ''ჳ' => 'Ⴣ''ჴ' => 'Ⴤ''ჵ' => 'Ⴥ''ⓐ' => 'Ⓐ''ⓑ' => 'Ⓑ',
159         
'ⓒ' => 'Ⓒ''ⓓ' => 'Ⓓ''ⓔ' => 'Ⓔ''ⓕ' => 'Ⓕ''ⓖ' => 'Ⓖ''ⓗ' => 'Ⓗ',
160         
'ⓘ' => 'Ⓘ''ⓙ' => 'Ⓙ''ⓚ' => 'Ⓚ''ⓛ' => 'Ⓛ''ⓜ' => 'Ⓜ''ⓝ' => 'Ⓝ',
161         
'ⓞ' => 'Ⓞ''ⓟ' => 'Ⓟ''ⓠ' => 'Ⓠ''ⓡ' => 'Ⓡ''ⓢ' => 'Ⓢ''ⓣ' => 'Ⓣ',
162         
'ⓤ' => 'Ⓤ''ⓥ' => 'Ⓥ''ⓦ' => 'Ⓦ''ⓧ' => 'Ⓧ''ⓨ' => 'Ⓨ''ⓩ' => 'Ⓩ'
163     
);
164     
165     
/**
166      * All uppercase UTF-8 characters not properly handled by [http://php.net/mb_strtolower mb_strtolower()] mapped to lowercase characters
167      * 
168      * @var array
169      */
170     
private static $_mb_upper_to_lower_fix = array(
171         
'ǝ' => 'ɘ''Dž' => 'dž''Lj' => 'lj''Nj' => 'nj''Ⴀ' => 'ა''Ⴁ' => 'ბ',
172         
'Ⴂ' => 'გ''Ⴃ' => 'დ''Ⴄ' => 'ე''Ⴅ' => 'ვ''Ⴆ' => 'ზ''Ⴇ' => 'თ',
173         
'Ⴈ' => 'ი''Ⴉ' => 'კ''Ⴊ' => 'ლ''Ⴋ' => 'მ''Ⴌ' => 'ნ''Ⴍ' => 'ო',
174         
'Ⴎ' => 'პ''Ⴏ' => 'ჟ''Ⴐ' => 'რ''Ⴑ' => 'ს''Ⴒ' => 'ტ''Ⴓ' => 'უ',
175         
'Ⴔ' => 'ფ''Ⴕ' => 'ქ''Ⴖ' => 'ღ''Ⴗ' => 'ყ''Ⴘ' => 'შ''Ⴙ' => 'ჩ',
176         
'Ⴚ' => 'ც''Ⴛ' => 'ძ''Ⴜ' => 'წ''Ⴝ' => 'ჭ''Ⴞ' => 'ხ''Ⴟ' => 'ჯ',
177         
'Ⴠ' => 'ჰ''Ⴡ' => 'ჱ''Ⴢ' => 'ჲ''Ⴣ' => 'ჳ''Ⴤ' => 'ჴ''Ⴥ' => 'ჵ',
178         
'ᾈ' => 'ᾀ''ᾉ' => 'ᾁ''ᾊ' => 'ᾂ''ᾋ' => 'ᾃ''ᾌ' => 'ᾄ''ᾍ' => 'ᾅ',
179         
'ᾎ' => 'ᾆ''ᾏ' => 'ᾇ''ᾘ' => 'ᾐ''ᾙ' => 'ᾑ''ᾚ' => 'ᾒ''ᾛ' => 'ᾓ',
180         
'ᾜ' => 'ᾔ''ᾝ' => 'ᾕ''ᾞ' => 'ᾖ''ᾟ' => 'ᾗ''ᾨ' => 'ᾠ''ᾩ' => 'ᾡ',
181         
'ᾪ' => 'ᾢ''ᾫ' => 'ᾣ''ᾬ' => 'ᾤ''ᾭ' => 'ᾥ''ᾮ' => 'ᾦ''ᾯ' => 'ᾧ',
182         
'Ⓐ' => 'ⓐ''Ⓑ' => 'ⓑ''Ⓒ' => 'ⓒ''Ⓓ' => 'ⓓ''Ⓔ' => 'ⓔ''Ⓕ' => 'ⓕ',
183         
'Ⓖ' => 'ⓖ''Ⓗ' => 'ⓗ''Ⓘ' => 'ⓘ''Ⓙ' => 'ⓙ''Ⓚ' => 'ⓚ''Ⓛ' => 'ⓛ',
184         
'Ⓜ' => 'ⓜ''Ⓝ' => 'ⓝ''Ⓞ' => 'ⓞ''Ⓟ' => 'ⓟ''Ⓠ' => 'ⓠ''Ⓡ' => 'ⓡ',
185         
'Ⓢ' => 'ⓢ''Ⓣ' => 'ⓣ''Ⓤ' => 'ⓤ''Ⓥ' => 'ⓥ''Ⓦ' => 'ⓦ''Ⓧ' => 'ⓧ',
186         
'Ⓨ' => 'ⓨ''Ⓩ' => 'ⓩ'
187     
);
188     
189     
/**
190      * All uppercase UTF-8 characters mapped to lowercase characters
191      * 
192      * @var array
193      */
194     
private static $_upper_to_lower = array(
195         
'A' => 'a''B' => 'b''C' => 'c''D' => 'd''E' => 'e''F' => 'f',
196         
'G' => 'g''H' => 'h''I' => 'i''J' => 'j''K' => 'k''L' => 'l',
197         
'M' => 'm''N' => 'n''O' => 'o''P' => 'p''Q' => 'q''R' => 'r',
198         
'S' => 's''T' => 't''U' => 'u''V' => 'v''W' => 'w''X' => 'x',
199         
'Y' => 'y''Z' => 'z''À' => 'à''Á' => 'á''Â' => 'â''Ã' => 'ã',
200         
'Ä' => 'ä''Å' => 'å''Æ' => 'æ''Ç' => 'ç''È' => 'è''É' => 'é',
201         
'Ê' => 'ê''Ë' => 'ë''Ì' => 'ì''Í' => 'í''Î' => 'î''Ï' => 'ï',
202         
'Ð' => 'ð''Ñ' => 'ñ''Ò' => 'ò''Ó' => 'ó''Ô' => 'ô''Õ' => 'õ',
203         
'Ö' => 'ö''Ø' => 'ø''Ù' => 'ù''Ú' => 'ú''Û' => 'û''Ü' => 'ü',
204         
'Ý' => 'ý''Þ' => 'þ''Ā' => 'ā''Ă' => 'ă''Ą' => 'ą''Ć' => 'ć',
205         
'Ĉ' => 'ĉ''Ċ' => 'ċ''Č' => 'č''Ď' => 'ď''Đ' => 'đ''Ē' => 'ē',
206         
'Ĕ' => 'ĕ''Ė' => 'ė''Ę' => 'ę''Ě' => 'ě''Ĝ' => 'ĝ''Ğ' => 'ğ',
207         
'Ġ' => 'ġ''Ģ' => 'ģ''Ĥ' => 'ĥ''Ħ' => 'ħ''Ĩ' => 'ĩ''Ī' => 'ī',
208         
'Ĭ' => 'ĭ''Į' => 'į''İ' => 'i''IJ' => 'ij''Ĵ' => 'ĵ''Ķ' => 'ķ',
209         
'Ĺ' => 'ĺ''Ļ' => 'ļ''Ľ' => 'ľ''Ŀ' => 'ŀ''Ł' => 'ł''Ń' => 'ń',
210         
'Ņ' => 'ņ''Ň' => 'ň''Ŋ' => 'ŋ''Ō' => 'ō''Ŏ' => 'ŏ''Ő' => 'ő',
211         
'Œ' => 'œ''Ŕ' => 'ŕ''Ŗ' => 'ŗ''Ř' => 'ř''Ś' => 'ś''Ŝ' => 'ŝ',
212         
'Ş' => 'ş''Š' => 'š''Ţ' => 'ţ''Ť' => 'ť''Ŧ' => 'ŧ''Ũ' => 'ũ',
213         
'Ū' => 'ū''Ŭ' => 'ŭ''Ů' => 'ů''Ű' => 'ű''Ų' => 'ų''Ŵ' => 'ŵ',
214         
'Ŷ' => 'ŷ''Ÿ' => 'ÿ''Ź' => 'ź''Ż' => 'ż''Ž' => 'ž''Ɓ' => 'ɓ',
215         
'Ƃ' => 'ƃ''Ƅ' => 'ƅ''Ɔ' => 'ɔ''Ƈ' => 'ƈ''Ɗ' => 'ɗ''Ƌ' => 'ƌ',
216         
'Ǝ' => 'ɘ''Ə' => 'ə''Ɛ' => 'ɛ''Ƒ' => 'ƒ''Ɠ' => 'ɠ''Ɣ' => 'ɣ',
217         
'Ɩ' => 'ɩ''Ɨ' => 'ɨ''Ƙ' => 'ƙ''Ɯ' => 'ɯ''Ɲ' => 'ɲ''Ɵ' => 'ɵ',
218         
'Ơ' => 'ơ''Ƣ' => 'ƣ''Ƥ' => 'ƥ''Ƨ' => 'ƨ''Ʃ' => 'ʃ''Ƭ' => 'ƭ',
219         
'Ʈ' => 'ʈ''Ư' => 'ư''Ʊ' => 'ʊ''Ʋ' => 'ʋ''Ƴ' => 'ƴ''Ƶ' => 'ƶ',
220         
'Ʒ' => 'ʒ''Ƹ' => 'ƹ''Ƽ' => 'ƽ''DŽ' => 'dž''Dž' => 'dž''LJ' => 'lj',
221         
'Lj' => 'lj''NJ' => 'nj''Nj' => 'nj''Ǎ' => 'ǎ''Ǐ' => 'ǐ''Ǒ' => 'ǒ',
222         
'Ǔ' => 'ǔ''Ǖ' => 'ǖ''Ǘ' => 'ǘ''Ǚ' => 'ǚ''Ǜ' => 'ǜ''Ǟ' => 'ǟ',
223         
'Ǡ' => 'ǡ''Ǣ' => 'ǣ''Ǥ' => 'ǥ''Ǧ' => 'ǧ''Ǩ' => 'ǩ''Ǫ' => 'ǫ',
224         
'Ǭ' => 'ǭ''Ǯ' => 'ǯ''DZ' => 'dz''Ǵ' => 'ǵ''Ǻ' => 'ǻ''Ǽ' => 'ǽ',
225         
'Ǿ' => 'ǿ''Ȁ' => 'ȁ''Ȃ' => 'ȃ''Ȅ' => 'ȅ''Ȇ' => 'ȇ''Ȉ' => 'ȉ',
226         
'Ȋ' => 'ȋ''Ȍ' => 'ȍ''Ȏ' => 'ȏ''Ȑ' => 'ȑ''Ȓ' => 'ȓ''Ȕ' => 'ȕ',
227         
'Ȗ' => 'ȗ''Ά' => 'ά''Έ' => 'έ''Ή' => 'ή''Ί' => 'ί''Ό' => 'ό',
228         
'Ύ' => 'ύ''Ώ' => 'ώ''Α' => 'α''Β' => 'β''Γ' => 'γ''Δ' => 'δ',
229         
'Ε' => 'ε''Ζ' => 'ζ''Η' => 'η''Θ' => 'θ''Ι' => 'ι''Κ' => 'κ',
230         
'Λ' => 'λ''Μ' => 'μ''Ν' => 'ν''Ξ' => 'ξ''Ο' => 'ο''Π' => 'π',
231         
'Ρ' => 'ρ''Σ' => 'σ''Τ' => 'τ''Υ' => 'υ''Φ' => 'φ''Χ' => 'χ',
232         
'Ψ' => 'ψ''Ω' => 'ω''Ϊ' => 'ϊ''Ϋ' => 'ϋ''Ϣ' => 'ϣ''Ϥ' => 'ϥ',
233         
'Ϧ' => 'ϧ''Ϩ' => 'ϩ''Ϫ' => 'ϫ''Ϭ' => 'ϭ''Ϯ' => 'ϯ''Ё' => 'ё',
234         
'Ђ' => 'ђ''Ѓ' => 'ѓ''Є' => 'є''Ѕ' => 'ѕ''І' => 'і''Ї' => 'ї',
235         
'Ј' => 'ј''Љ' => 'љ''Њ' => 'њ''Ћ' => 'ћ''Ќ' => 'ќ''Ў' => 'ў',
236         
'Џ' => 'џ''А' => 'а''Б' => 'б''В' => 'в''Г' => 'г''Д' => 'д',
237         
'Е' => 'е''Ж' => 'ж''З' => 'з''И' => 'и''Й' => 'й''К' => 'к',
238         
'Л' => 'л''М' => 'м''Н' => 'н''О' => 'о''П' => 'п''Р' => 'р',
239         
'С' => 'с''Т' => 'т''У' => 'у''Ф' => 'ф''Х' => 'х''Ц' => 'ц',
240         
'Ч' => 'ч''Ш' => 'ш''Щ' => 'щ''Ъ' => 'ъ''Ы' => 'ы''Ь' => 'ь',
241         
'Э' => 'э''Ю' => 'ю''Я' => 'я''Ѡ' => 'ѡ''Ѣ' => 'ѣ''Ѥ' => 'ѥ',
242         
'Ѧ' => 'ѧ''Ѩ' => 'ѩ''Ѫ' => 'ѫ''Ѭ' => 'ѭ''Ѯ' => 'ѯ''Ѱ' => 'ѱ',
243         
'Ѳ' => 'ѳ''Ѵ' => 'ѵ''Ѷ' => 'ѷ''Ѹ' => 'ѹ''Ѻ' => 'ѻ''Ѽ' => 'ѽ',
244         
'Ѿ' => 'ѿ''Ҁ' => 'ҁ''Ґ' => 'ґ''Ғ' => 'ғ''Ҕ' => 'ҕ''Җ' => 'җ',
245         
'Ҙ' => 'ҙ''Қ' => 'қ''Ҝ' => 'ҝ''Ҟ' => 'ҟ''Ҡ' => 'ҡ''Ң' => 'ң',
246         
'Ҥ' => 'ҥ''Ҧ' => 'ҧ''Ҩ' => 'ҩ''Ҫ' => 'ҫ''Ҭ' => 'ҭ''Ү' => 'ү',
247         
'Ұ' => 'ұ''Ҳ' => 'ҳ''Ҵ' => 'ҵ''Ҷ' => 'ҷ''Ҹ' => 'ҹ''Һ' => 'һ',
248         
'Ҽ' => 'ҽ''Ҿ' => 'ҿ''Ӂ' => 'ӂ''Ӄ' => 'ӄ''Ӈ' => 'ӈ''Ӌ' => 'ӌ',
249         
'Ӑ' => 'ӑ''Ӓ' => 'ӓ''Ӕ' => 'ӕ''Ӗ' => 'ӗ''Ә' => 'ә''Ӛ' => 'ӛ',
250         
'Ӝ' => 'ӝ''Ӟ' => 'ӟ''Ӡ' => 'ӡ''Ӣ' => 'ӣ''Ӥ' => 'ӥ''Ӧ' => 'ӧ',
251         
'Ө' => 'ө''Ӫ' => 'ӫ''Ӯ' => 'ӯ''Ӱ' => 'ӱ''Ӳ' => 'ӳ''Ӵ' => 'ӵ',
252         
'Ӹ' => 'ӹ''Ա' => 'ա''Բ' => 'բ''Գ' => 'գ''Դ' => 'դ''Ե' => 'ե',
253         
'Զ' => 'զ''Է' => 'է''Ը' => 'ը''Թ' => 'թ''Ժ' => 'ժ''Ի' => 'ի',
254         
'Լ' => 'լ''Խ' => 'խ''Ծ' => 'ծ''Կ' => 'կ''Հ' => 'հ''Ձ' => 'ձ',
255         
'Ղ' => 'ղ''Ճ' => 'ճ''Մ' => 'մ''Յ' => 'յ''Ն' => 'ն''Շ' => 'շ',
256         
'Ո' => 'ո''Չ' => 'չ''Պ' => 'պ''Ջ' => 'ջ''Ռ' => 'ռ''Ս' => 'ս',
257         
'Վ' => 'վ''Տ' => 'տ''Ր' => 'ր''Ց' => 'ց''Ւ' => 'ւ''Փ' => 'փ',
258         
'Ք' => 'ք''Օ' => 'օ''Ֆ' => 'ֆ''Ⴀ' => 'ა''Ⴁ' => 'ბ''Ⴂ' => 'გ',
259         
'Ⴃ' => 'დ''Ⴄ' => 'ე''Ⴅ' => 'ვ''Ⴆ' => 'ზ''Ⴇ' => 'თ''Ⴈ' => 'ი',
260         
'Ⴉ' => 'კ''Ⴊ' => 'ლ''Ⴋ' => 'მ''Ⴌ' => 'ნ''Ⴍ' => 'ო''Ⴎ' => 'პ',
261         
'Ⴏ' => 'ჟ''Ⴐ' => 'რ''Ⴑ' => 'ს''Ⴒ' => 'ტ''Ⴓ' => 'უ''Ⴔ' => 'ფ',
262         
'Ⴕ' => 'ქ''Ⴖ' => 'ღ''Ⴗ' => 'ყ''Ⴘ' => 'შ''Ⴙ' => 'ჩ''Ⴚ' => 'ც',
263         
'Ⴛ' => 'ძ''Ⴜ' => 'წ''Ⴝ' => 'ჭ''Ⴞ' => 'ხ''Ⴟ' => 'ჯ''Ⴠ' => 'ჰ',
264         
'Ⴡ' => 'ჱ''Ⴢ' => 'ჲ''Ⴣ' => 'ჳ''Ⴤ' => 'ჴ''Ⴥ' => 'ჵ''Ḁ' => 'ḁ',
265         
'Ḃ' => 'ḃ''Ḅ' => 'ḅ''Ḇ' => 'ḇ''Ḉ' => 'ḉ''Ḋ' => 'ḋ''Ḍ' => 'ḍ',
266         
'Ḏ' => 'ḏ''Ḑ' => 'ḑ''Ḓ' => 'ḓ''Ḕ' => 'ḕ''Ḗ' => 'ḗ''Ḙ' => 'ḙ',
267         
'Ḛ' => 'ḛ''Ḝ' => 'ḝ''Ḟ' => 'ḟ''Ḡ' => 'ḡ''Ḣ' => 'ḣ''Ḥ' => 'ḥ',
268         
'Ḧ' => 'ḧ''Ḩ' => 'ḩ''Ḫ' => 'ḫ''Ḭ' => 'ḭ''Ḯ' => 'ḯ''Ḱ' => 'ḱ',
269         
'Ḳ' => 'ḳ''Ḵ' => 'ḵ''Ḷ' => 'ḷ''Ḹ' => 'ḹ''Ḻ' => 'ḻ''Ḽ' => 'ḽ',
270         
'Ḿ' => 'ḿ''Ṁ' => 'ṁ''Ṃ' => 'ṃ''Ṅ' => 'ṅ''Ṇ' => 'ṇ''Ṉ' => 'ṉ',
271         
'Ṋ' => 'ṋ''Ṍ' => 'ṍ''Ṏ' => 'ṏ''Ṑ' => 'ṑ''Ṓ' => 'ṓ''Ṕ' => 'ṕ',
272         
'Ṗ' => 'ṗ''Ṙ' => 'ṙ''Ṛ' => 'ṛ''Ṝ' => 'ṝ''Ṟ' => 'ṟ''Ṡ' => 'ṡ',
273         
'Ṣ' => 'ṣ''Ṥ' => 'ṥ''Ṧ' => 'ṧ''Ṩ' => 'ṩ''Ṫ' => 'ṫ''Ṭ' => 'ṭ',
274         
'Ṯ' => 'ṯ''Ṱ' => 'ṱ''Ṳ' => 'ṳ''Ṵ' => 'ṵ''Ṷ' => 'ṷ''Ṹ' => 'ṹ',
275         
'Ṻ' => 'ṻ''Ṽ' => 'ṽ''Ṿ' => 'ṿ''Ẁ' => 'ẁ''Ẃ' => 'ẃ''Ẅ' => 'ẅ',
276         
'Ẇ' => 'ẇ''Ẉ' => 'ẉ''Ẋ' => 'ẋ''Ẍ' => 'ẍ''Ẏ' => 'ẏ''Ẑ' => 'ẑ',
277         
'Ẓ' => 'ẓ''Ẕ' => 'ẕ''Ạ' => 'ạ''Ả' => 'ả''Ấ' => 'ấ''Ầ' => 'ầ',
278         
'Ẩ' => 'ẩ''Ẫ' => 'ẫ''Ậ' => 'ậ''Ắ' => 'ắ''Ằ' => 'ằ''Ẳ' => 'ẳ',
279         
'Ẵ' => 'ẵ''Ặ' => 'ặ''Ẹ' => 'ẹ''Ẻ' => 'ẻ''Ẽ' => 'ẽ''Ế' => 'ế',
280         
'Ề' => 'ề''Ể' => 'ể''Ễ' => 'ễ''Ệ' => 'ệ''Ỉ' => 'ỉ''Ị' => 'ị',
281         
'Ọ' => 'ọ''Ỏ' => 'ỏ''Ố' => 'ố''Ồ' => 'ồ''Ổ' => 'ổ''Ỗ' => 'ỗ',
282         
'Ộ' => 'ộ''Ớ' => 'ớ''Ờ' => 'ờ''Ở' => 'ở''Ỡ' => 'ỡ''Ợ' => 'ợ',
283         
'Ụ' => 'ụ''Ủ' => 'ủ''Ứ' => 'ứ''Ừ' => 'ừ''Ử' => 'ử''Ữ' => 'ữ',
284         
'Ự' => 'ự''Ỳ' => 'ỳ''Ỵ' => 'ỵ''Ỷ' => 'ỷ''Ỹ' => 'ỹ''Ἀ' => 'ἀ',
285         
'Ἁ' => 'ἁ''Ἂ' => 'ἂ''Ἃ' => 'ἃ''Ἄ' => 'ἄ''Ἅ' => 'ἅ''Ἆ' => 'ἆ',
286         
'Ἇ' => 'ἇ''Ἐ' => 'ἐ''Ἑ' => 'ἑ''Ἒ' => 'ἒ''Ἓ' => 'ἓ''Ἔ' => 'ἔ',
287         
'Ἕ' => 'ἕ''Ἠ' => 'ἠ''Ἡ' => 'ἡ''Ἢ' => 'ἢ''Ἣ' => 'ἣ''Ἤ' => 'ἤ',
288         
'Ἥ' => 'ἥ''Ἦ' => 'ἦ''Ἧ' => 'ἧ''Ἰ' => 'ἰ''Ἱ' => 'ἱ''Ἲ' => 'ἲ',
289         
'Ἳ' => 'ἳ''Ἴ' => 'ἴ''Ἵ' => 'ἵ''Ἶ' => 'ἶ''Ἷ' => 'ἷ''Ὀ' => 'ὀ',
290         
'Ὁ' => 'ὁ''Ὂ' => 'ὂ''Ὃ' => 'ὃ''Ὄ' => 'ὄ''Ὅ' => 'ὅ''Ὑ' => 'ὑ',
291         
'Ὓ' => 'ὓ''Ὕ' => 'ὕ''Ὗ' => 'ὗ''Ὠ' => 'ὠ''Ὡ' => 'ὡ''Ὢ' => 'ὢ',
292         
'Ὣ' => 'ὣ''Ὤ' => 'ὤ''Ὥ' => 'ὥ''Ὦ' => 'ὦ''Ὧ' => 'ὧ''ᾈ' => 'ᾀ',
293         
'ᾉ' => 'ᾁ''ᾊ' => 'ᾂ''ᾋ' => 'ᾃ''ᾌ' => 'ᾄ''ᾍ' => 'ᾅ''ᾎ' => 'ᾆ',
294         
'ᾏ' => 'ᾇ''ᾘ' => 'ᾐ''ᾙ' => 'ᾑ''ᾚ' => 'ᾒ''ᾛ' => 'ᾓ''ᾜ' => 'ᾔ',
295         
'ᾝ' => 'ᾕ''ᾞ' => 'ᾖ''ᾟ' => 'ᾗ''ᾨ' => 'ᾠ''ᾩ' => 'ᾡ''ᾪ' => 'ᾢ',
296         
'ᾫ' => 'ᾣ''ᾬ' => 'ᾤ''ᾭ' => 'ᾥ''ᾮ' => 'ᾦ''ᾯ' => 'ᾧ''Ᾰ' => 'ᾰ',
297         
'Ᾱ' => 'ᾱ''Ῐ' => 'ῐ''Ῑ' => 'ῑ''Ῠ' => 'ῠ''Ῡ' => 'ῡ''Ⓐ' => 'ⓐ',
298         
'Ⓑ' => 'ⓑ''Ⓒ' => 'ⓒ''Ⓓ' => 'ⓓ''Ⓔ' => 'ⓔ''Ⓕ' => 'ⓕ''Ⓖ' => 'ⓖ',
299         
'Ⓗ' => 'ⓗ''Ⓘ' => 'ⓘ''Ⓙ' => 'ⓙ''Ⓚ' => 'ⓚ''Ⓛ' => 'ⓛ''Ⓜ' => 'ⓜ',
300         
'Ⓝ' => 'ⓝ''Ⓞ' => 'ⓞ''Ⓟ' => 'ⓟ''Ⓠ' => 'ⓠ''Ⓡ' => 'ⓡ''Ⓢ' => 'ⓢ',
301         
'Ⓣ' => 'ⓣ''Ⓤ' => 'ⓤ''Ⓥ' => 'ⓥ''Ⓦ' => 'ⓦ''Ⓧ' => 'ⓧ''Ⓨ' => 'ⓨ',
302         
'Ⓩ' => 'ⓩ''A' => 'a''B' => 'b''C' => 'c''D' => 'd''E' => 'e',
303         
'F' => 'f''G' => 'g''H' => 'h''I' => 'i''J' => 'j''K' => 'k',
304         
'L' => 'l''M' => 'm''N' => 'n''O' => 'o''P' => 'p''Q' => 'q',
305         
'R' => 'r''S' => 's''T' => 't''U' => 'u''V' => 'v''W' => 'w',
306         
'X' => 'x''Y' => 'y''Z' => 'z'
307     
);
308     
309     
/**
310      * A mapping of all ASCII-based latin characters, puntuation, symbols and number forms to ASCII.
311      * 
312      * Includes elements form the following unicode blocks:
313      * 
314      *  - Latin-1 Supplement
315      *  - Latin Extended-A
316      *  - Latin Extended-B
317      *  - IPA Extensions
318      *  - Latin Extended Additional
319      *  - General Punctuation
320      *  - Letterlike symbols
321      *  - Number Forms
322      * 
323      * @var array
324      */
325     
private static $_utf8_to_ascii = array(
326         
// Latin-1 Supplement
327         
'©' => '(c)''«' => '<<',  '®' => '(R)''»' => '>>',  '¼' => '1/4',
328         
'½' => '1/2''¾' => '3/4''À' => 'A',   'Á' => 'A',   'Â' => 'A',
329         
'Ã' => 'A',   'Ä' => 'A',   'Å' => 'A',   'Æ' => 'AE',  'Ç' => 'C',
330         
'È' => 'E',   'É' => 'E',   'Ê' => 'E',   'Ë' => 'E',   'Ì' => 'I',
331         
'Í' => 'I',   'Î' => 'I',   'Ï' => 'I',   'Ñ' => 'N',   'Ò' => 'O',
332         
'Ó' => 'O',   'Ô' => 'O',   'Õ' => 'O',   'Ö' => 'O',   'Ø' => 'O',
333         
'Ù' => 'U',   'Ú' => 'U',   'Û' => 'U',   'Ü' => 'U',   'Ý' => 'Y',
334         
'à' => 'a',   'á' => 'a',   'â' => 'a',   'ã' => 'a',   'ä' => 'a',
335         
'å' => 'a',   'æ' => 'ae',  'ç' => 'c',   'è' => 'e',   'é' => 'e',
336         
'ê' => 'e',   'ë' => 'e',   'ì' => 'i',   'í' => 'i',   'î' => 'i',
337         
'ï' => 'i',   'ñ' => 'n',   'ò' => 'o',   'ó' => 'o',   'ô' => 'o',
338         
'õ' => 'o',   'ö' => 'o',   'ø' => 'o',   'ù' => 'u',   'ú' => 'u',
339         
'û' => 'u',   'ü' => 'u',   'ý' => 'y',   'ÿ' => 'y',
340         
// Latin Extended-A
341         
'Ā' => 'A',   'ā' => 'a',   'Ă' => 'A',   'ă' => 'a',   'Ą' => 'A',
342         
'ą' => 'a',   'Ć' => 'C',   'ć' => 'c',   'Ĉ' => 'C',   'ĉ' => 'c',
343         
'Ċ' => 'C',   'ċ' => 'c',   'Č' => 'C',   'č' => 'c',   'Ď' => 'D',
344         
'ď' => 'd',   'Đ' => 'D',   'đ' => 'd',   'Ē' => 'E',   'ē' => 'e',
345         
'Ĕ' => 'E',   'ĕ' => 'e',   'Ė' => 'E',   'ė' => 'e',   'Ę' => 'E',
346         
'ę' => 'e',   'Ě' => 'E',   'ě' => 'e',   'Ĝ' => 'G',   'ĝ' => 'g',
347         
'Ğ' => 'G',   'ğ' => 'g',   'Ġ' => 'G',   'ġ' => 'g',   'Ģ' => 'G',
348         
'ģ' => 'g',   'Ĥ' => 'H',   'ĥ' => 'h',   'Ħ' => 'H',   'ħ' => 'h',
349         
'Ĩ' => 'I',   'ĩ' => 'i',   'Ī' => 'I',   'ī' => 'i',   'Ĭ' => 'I',
350         
'ĭ' => 'i',   'Į' => 'I',   'į' => 'i',   'İ' => 'I',   'ı' => 'i',
351         
'IJ' => 'IJ',  'ij' => 'ij',  'Ĵ' => 'J',   'ĵ' => 'j',   'Ķ' => 'K',
352         
'ķ' => 'k',   'Ĺ' => 'L',   'ĺ' => 'l',   'Ļ' => 'L',   'ļ' => 'l',
353         
'Ľ' => 'L',   'ľ' => 'l',   'Ŀ' => 'L',   'ŀ' => 'l',   'Ł' => 'L',
354         
'ł' => 'l',   'Ń' => 'N',   'ń' => 'n',   'Ņ' => 'N',   'ņ' => 'n',
355         
'Ň' => 'N',   'ň' => 'n',   'ʼn' => "'n"'Ŋ' => 'N',   'ŋ' => 'n',
356         
'Ō' => 'O',   'ō' => 'o',   'Ŏ' => 'O',   'ŏ' => 'o',   'Ő' => 'O',
357         
'ő' => 'o',   'Œ' => 'OE',  'œ' => 'oe',  'Ŕ' => 'R',   'ŕ' => 'r',
358         
'Ŗ' => 'R',   'ŗ' => 'r',   'Ř' => 'R',   'ř' => 'r',   'Ś' => 'S',
359         
'ś' => 's',   'Ŝ' => 'S',   'ŝ' => 's',   'Ş' => 'S',   'ş' => 's',
360         
'Š' => 'S',   'š' => 's',   'Ţ' => 'T',   'ţ' => 't',   'Ť' => 'T',
361         
'ť' => 't',   'Ŧ' => 'T',   'ŧ' => 't',   'Ũ' => 'U',   'ũ' => 'u',
362         
'Ū' => 'U',   'ū' => 'u',   'Ŭ' => 'U',   'ŭ' => 'u',   'Ů' => 'U',
363         
'ů' => 'u',   'Ű' => 'U',   'ű' => 'u',   'Ų' => 'U',   'ų' => 'u',
364         
'Ŵ' => 'W',   'ŵ' => 'w',   'Ŷ' => 'Y',   'ŷ' => 'y',   'Ÿ' => 'Y',
365         
'Ź' => 'Z',   'ź' => 'z',   'Ż' => 'Z',   'ż' => 'z',   'Ž' => 'Z',
366         
'ž' => 'z',
367         
// Latin Extended-B
368         
'ƀ' => 'b',   'Ɓ' => 'B',   'Ƃ' => 'B',   'ƃ' => 'b',   'Ɔ' => 'O',
369         
'Ƈ' => 'C',   'ƈ' => 'c',   'Ɖ' => 'D',   'Ɗ' => 'D',   'Ƌ' => 'D',
370         
'ƌ' => 'd',   'Ǝ' => 'E',   'Ɛ' => 'E',   'Ƒ' => 'F',   'ƒ' => 'f',
371         
'Ɠ' => 'G',   'Ɨ' => 'I',   'Ƙ' => 'K',   'ƙ' => 'k',   'ƚ' => 'l',
372         
'Ɯ' => 'M',   'Ɲ' => 'N',   'ƞ' => 'n',   'Ɵ' => 'O',   'Ơ' => 'O',
373         
'ơ' => 'o',   'Ƣ' => 'OI',  'ƣ' => 'oi',  'Ƥ' => 'P',   'ƥ' => 'p',
374         
'ƫ' => 't',   'Ƭ' => 'T',   'ƭ' => 't',   'Ʈ' => 'T',   'Ư' => 'U',
375         
'ư' => 'u',   'Ʋ' => 'V',   'Ƴ' => 'Y',   'ƴ' => 'y',   'Ƶ' => 'Z',
376         
'ƶ' => 'z',   'ƻ' => '2',   'DŽ' => 'DZ',  'Dž' => 'Dz',  'dž' => 'dz',
377         
'LJ' => 'LJ',  'Lj' => 'Lj',  'lj' => 'lj',  'NJ' => 'Nj',  'Nj' => 'Nj',
378         
'nj' => 'nj',  'Ǎ' => 'A',   'ǎ' => 'a',   'Ǐ' => 'I',   'ǐ' => 'i',
379         
'Ǒ' => 'O',   'ǒ' => 'o',   'Ǔ' => 'U',   'ǔ' => 'u',   'Ǖ' => 'U',
380         
'ǖ' => 'u',   'Ǘ' => 'U',   'ǘ' => 'u',   'Ǚ' => 'U',   'ǚ' => 'u',
381         
'Ǜ' => 'U',   'ǜ' => 'u',   'ǝ' => 'e',   'Ǟ' => 'A',   'ǟ' => 'a',
382         
'Ǡ' => 'A',   'ǡ' => 'a',   'Ǣ' => 'AE',  'ǣ' => 'ae',  'Ǥ' => 'G',
383         
'ǥ' => 'g',   'Ǧ' => 'G',   'ǧ' => 'g',   'Ǩ' => 'K',   'ǩ' => 'k',
384         
'Ǫ' => 'O',   'ǫ' => 'o',   'Ǭ' => 'O',   'ǭ' => 'o',   'ǰ' => 'j',
385         
'DZ' => 'DZ',  'Dz' => 'Dz',  'dz' => 'dz',  'Ǵ' => 'G',   'ǵ' => 'g',
386         
'Ǹ' => 'N',   'ǹ' => 'n',   'Ǻ' => 'A',   'ǻ' => 'a',   'Ǽ' => 'AE',
387         
'ǽ' => 'ae',  'Ǿ' => 'O',   'ǿ' => 'o',   'Ȁ' => 'A',   'ȁ' => 'a',
388         
'Ȃ' => 'A',   'ȃ' => 'a',   'Ȅ' => 'E',   'ȅ' => 'e',   'Ȇ' => 'E',
389         
'ȇ' => 'e',   'Ȉ' => 'I',   'ȉ' => 'i',   'Ȋ' => 'I',   'ȋ' => 'i',
390         
'Ȍ' => 'O',   'ȍ' => 'o',   'Ȏ' => 'O',   'ȏ' => 'o',   'Ȑ' => 'R',
391         
'ȑ' => 'r',   'Ȓ' => 'R',   'ȓ' => 'r',   'Ȕ' => 'U',   'ȕ' => 'u',
392         
'Ȗ' => 'U',   'ȗ' => 'u',   'Ș' => 'S',   'ș' => 's',   'Ț' => 'T',
393         
'ț' => 't',   'Ȟ' => 'H',   'ȟ' => 'h',   'Ƞ' => 'N',   'ȡ' => 'd',
394         
'Ȥ' => 'Z',   'ȥ' => 'z',   'Ȧ' => 'A',   'ȧ' => 'a',   'Ȩ' => 'E',
395         
'ȩ' => 'e',   'Ȫ' => 'O',   'ȫ' => 'o',   'Ȭ' => 'O',   'ȭ' => 'o',
396         
'Ȯ' => 'O',   'ȯ' => 'o',   'Ȱ' => 'O',   'ȱ' => 'o',   'Ȳ' => 'Y',
397         
'ȳ' => 'y',   'ȴ' => 'l',   'ȵ' => 'n',   'ȶ' => 't',   'ȷ' => 'j',
398         
'ȸ' => 'db',  'ȹ' => 'qp',  'Ⱥ' => 'A',   'Ȼ' => 'C',   'ȼ' => 'c',
399         
'Ƚ' => 'L',   'Ⱦ' => 'T',   'ȿ' => 's',   'ɀ' => 'z',   'Ƀ' => 'B',
400         
'Ʉ' => 'U',   'Ʌ' => 'V',   'Ɇ' => 'E',   'ɇ' => 'e',   'Ɉ' => 'J',
401         
'ɉ' => 'j',   'Ɋ' => 'Q',   'ɋ' => 'q',   'Ɍ' => 'R',   'ɍ' => 'r',
402         
'Ɏ' => 'Y',   'ɏ' => 'y',
403         
// IPA Extensions
404         
'ɐ' => 'a',   'ɓ' => 'b',   'ɔ' => 'o',   'ɕ' => 'c',   'ɖ' => 'd',
405         
'ɗ' => 'd',   'ɘ' => 'e',   'ɛ' => 'e',   'ɜ' => 'e',   'ɝ' => 'e',
406         
'ɞ' => 'e',   'ɟ' => 'j',   'ɠ' => 'g',   'ɡ' => 'g',   'ɢ' => 'G',
407         
'ɥ' => 'h',   'ɦ' => 'h',   'ɨ' => 'i',   'ɪ' => 'I',   'ɫ' => 'l',
408         
'ɬ' => 'l',   'ɭ' => 'l',   'ɯ' => 'm',   'ɰ' => 'm',   'ɱ' => 'm',
409         
'ɲ' => 'n',   'ɳ' => 'n',   'ɴ' => 'N',   'ɵ' => 'o',   'ɶ' => 'OE',
410         
'ɹ' => 'r',   'ɺ' => 'r',   'ɻ' => 'r',   'ɼ' => 'r',   'ɽ' => 'r',
411         
'ɾ' => 'r',   'ɿ' => 'r',   'ʀ' => 'R',   'ʁ' => 'R',   'ʂ' => 's',
412         
'ʇ' => 't',   'ʈ' => 't',   'ʉ' => 'u',   'ʋ' => 'v',   'ʌ' => 'v',
413         
'ʍ' => 'w',   'ʎ' => 'y',   'ʏ' => 'Y',   'ʐ' => 'z',   'ʑ' => 'z',
414         
'ʗ' => 'C',   'ʙ' => 'B',   'ʚ' => 'e',   'ʛ' => 'G',   'ʜ' => 'H',
415         
'ʝ' => 'j',   'ʞ' => 'k',   'ʟ' => 'L',   'ʠ' => 'q',   'ʣ' => 'dz',
416         
'ʥ' => 'dz',  'ʦ' => 'ts',  'ʨ' => 'tc',  'ʪ' => 'ls',  'ʫ' => 'lz',
417         
'ʮ' => 'h',   'ʯ' => 'h',
418         
// Latin Extended Additional
419         
'Ḁ' => 'A',   'ḁ' => 'a',   'Ḃ' => 'B',   'ḃ' => 'b',   'Ḅ' => 'B',
420         
'ḅ' => 'b',   'Ḇ' => 'B',   'ḇ' => 'b',   'Ḉ' => 'C',   'ḉ' => 'c',
421         
'Ḋ' => 'D',   'ḋ' => 'd',   'Ḍ' => 'D',   'ḍ' => 'd',   'Ḏ' => 'D',
422         
'ḏ' => 'd',   'Ḑ' => 'D',   'ḑ' => 'd',   'Ḓ' => 'D',   'ḓ' => 'd',
423         
'Ḕ' => 'E',   'ḕ' => 'e',   'Ḗ' => 'E',   'ḗ' => 'e',   'Ḙ' => 'E',
424         
'ḙ' => 'e',   'Ḛ' => 'E',   'ḛ' => 'e',   'Ḝ' => 'E',   'ḝ' => 'e',
425         
'Ḟ' => 'F',   'ḟ' => 'f',   'Ḡ' => 'G',   'ḡ' => 'g',   'Ḣ' => 'H',
426         
'ḣ' => 'h',   'Ḥ' => 'H',   'ḥ' => 'h',   'Ḧ' => 'H',   'ḧ' => 'h',
427         
'Ḩ' => 'H',   'ḩ' => 'h',   'Ḫ' => 'H',   'ḫ' => 'h',   'Ḭ' => 'I',
428         
'ḭ' => 'i',   'Ḯ' => 'I',   'ḯ' => 'i',   'Ḱ' => 'K',   'ḱ' => 'k',
429         
'Ḳ' => 'K',   'ḳ' => 'k',   'Ḵ' => 'K',   'ḵ' => 'k',   'Ḷ' => 'L',
430         
'ḷ' => 'l',   'Ḹ' => 'L',   'ḹ' => 'l',   'Ḻ' => 'L',   'ḻ' => 'l',
431         
'Ḽ' => 'L',   'ḽ' => 'l',   'Ḿ' => 'M',   'ḿ' => 'm',   'Ṁ' => 'M',
432         
'ṁ' => 'm',   'Ṃ' => 'M',   'ṃ' => 'm',   'Ṅ' => 'N',   'ṅ' => 'n',
433         
'Ṇ' => 'N',   'ṇ' => 'n',   'Ṉ' => 'N',   'ṉ' => 'n',   'Ṋ' => 'N',
434         
'ṋ' => 'n',   'Ṍ' => 'O',   'ṍ' => 'o',   'Ṏ' => 'O',   'ṏ' => 'o',
435         
'Ṑ' => 'O',   'ṑ' => 'o',   'Ṓ' => 'O',   'ṓ' => 'o',   'Ṕ' => 'P',
436         
'ṕ' => 'p',   'Ṗ' => 'P',   'ṗ' => 'p',   'Ṙ' => 'R',   'ṙ' => 'r',
437         
'Ṛ' => 'R',   'ṛ' => 'r',   'Ṝ' => 'R',   'ṝ' => 'r',   'Ṟ' => 'R',
438         
'ṟ' => 'r',   'Ṡ' => 'S',   'ṡ' => 's',   'Ṣ' => 'S',   'ṣ' => 's',
439         
'Ṥ' => 'S',   'ṥ' => 's',   'Ṧ' => 'S',   'ṧ' => 's',   'Ṩ' => 'S',
440         
'ṩ' => 's',   'Ṫ' => 'T',   'ṫ' => 't',   'Ṭ' => 'T',   'ṭ' => 't',
441         
'Ṯ' => 'T',   'ṯ' => 't',   'Ṱ' => 'T',   'ṱ' => 't',   'Ṳ' => 'U',
442         
'ṳ' => 'u',   'Ṵ' => 'U',   'ṵ' => 'u',   'Ṷ' => 'U',   'ṷ' => 'u',
443         
'Ṹ' => 'U',   'ṹ' => 'u',   'Ṻ' => 'U',   'ṻ' => 'u',   'Ṽ' => 'V',
444         
'ṽ' => 'v',   'Ṿ' => 'V',   'ṿ' => 'v',   'Ẁ' => 'W',   'ẁ' => 'w',
445         
'Ẃ' => 'W',   'ẃ' => 'w',   'Ẅ' => 'W',   'ẅ' => 'w',   'Ẇ' => 'W',
446         
'ẇ' => 'w',   'Ẉ' => 'W',   'ẉ' => 'w',   'Ẋ' => 'X',   'ẋ' => 'x',
447         
'Ẍ' => 'X',   'ẍ' => 'x',   'Ẏ' => 'Y',   'ẏ' => 'y',   'Ẑ' => 'Z',
448         
'ẑ' => 'z',   'Ẓ' => 'Z',   'ẓ' => 'z',   'Ẕ' => 'Z',   'ẕ' => 'z',
449         
'ẖ' => 'h',   'ẗ' => 't',   'ẘ' => 'w',   'ẙ' => 'y',   'ẚ' => 'a',
450         
'Ạ' => 'A',   'ạ' => 'a',   'Ả' => 'A',   'ả' => 'a',   'Ấ' => 'A',
451         
'ấ' => 'a',   'Ầ' => 'A',   'ầ' => 'a',   'Ẩ' => 'A',   'ẩ' => 'a',
452         
'Ẫ' => 'A',   'ẫ' => 'a',   'Ậ' => 'A',   'ậ' => 'a',   'Ắ' => 'A',
453         
'ắ' => 'a',   'Ằ' => 'A',   'ằ' => 'a',   'Ẳ' => 'A',   'ẳ' => 'a',
454         
'Ẵ' => 'A',   'ẵ' => 'a',   'Ặ' => 'A',   'ặ' => 'a',   'Ẹ' => 'E',
455         
'ẹ' => 'e',   'Ẻ' => 'E',   'ẻ' => 'e',   'Ẽ' => 'E',   'ẽ' => 'e',
456         
'Ế' => 'E',   'ế' => 'e',   'Ề' => 'E',   'ề' => 'e',   'Ể' => 'E',
457         
'ể' => 'e',   'Ễ' => 'E',   'ễ' => 'e',   'Ệ' => 'E',   'ệ' => 'e',
458         
'Ỉ' => 'I',   'ỉ' => 'i',   'Ị' => 'I',   'ị' => 'i',   'Ọ' => 'O',
459         
'ọ' => 'o',   'Ỏ' => 'O',   'ỏ' => 'o',   'Ố' => 'O',   'ố' => 'o',
460         
'Ồ' => 'O',   'ồ' => 'o',   'Ổ' => 'O',   'ổ' => 'o',   'Ỗ' => 'O',
461         
'ỗ' => 'o',   'Ộ' => 'O',   'ộ' => 'o',   'Ớ' => 'O',   'ớ' => 'o',
462         
'Ờ' => 'O',   'ờ' => 'o',   'Ở' => 'O',   'ở' => 'o',   'Ỡ' => 'O',
463         
'ỡ' => 'o',   'Ợ' => 'O',   'ợ' => 'o',   'Ụ' => 'U',   'ụ' => 'u',
464         
'Ủ' => 'U',   'ủ' => 'u',   'Ứ' => 'U',   'ứ' => 'u',   'Ừ' => 'U',
465         
'ừ' => 'u',   'Ử' => 'U',   'ử' => 'u',   'Ữ' => 'U',   'ữ' => 'u',
466         
'Ự' => 'U',   'ự' => 'u',   'Ỳ' => 'Y',   'ỳ' => 'y',   'Ỵ' => 'Y',
467         
'ỵ' => 'y',   'Ỷ' => 'Y',   'ỷ' => 'y',   'Ỹ' => 'Y',   'ỹ' => 'y',
468         
// General Punctuation
469         
' ' => ' ',   ' ' => ' ',   ' ' => ' ',   ' ' => ' ',   ' ' => ' ',
470         
' ' => ' ',   ' ' => ' ',   ' ' => ' ',   ' ' => ' ',   ' ' => ' ',
471         
' ' => ' ',   '​' => '',    '‌' => '',    '‍' => '',    '‐' => '-',
472         
'‑' => '-',   '‒' => '-',   '–' => '-',   '—' => '-',   '―' => '-',
473         
'‖' => '||',  '‘' => "'",   '’' => "'",   '‚' => ',',   '‛' => "'",
474         
'“' => '"',   '”' => '"',   '‟' => '"',   '․' => '.',   '‥' => '..',
475         
'…' => '...'' ' => ' ',   '′' => "'",   '″' => '"',   '‴' => '\'"',
476         
'‵' => "'",   '‶' => '"',   '‷' => '"\'''‹' => '<',   '›' => '>',
477         
'‼' => '!!',  '‽' => '?!',  '⁄' => '/',   '⁇' => '?/',  '⁈' => '?!',
478         
'⁉' => '!?',
479         
// Letterlike Symbols
480         
'℠' => 'SM',  '™' => 'TM',
481         
// Number Forms
482         
'⅓' => '1/3''⅔' => '2/3''⅕' => '1/5''⅖' => '2/5''⅗' => '3/5',
483         
'⅘' => '4/5''⅙' => '1/6''⅚' => '5/6''⅛' => '1/8''⅜' => '3/8',
484         
'⅝' => '5/8''⅞' => '7/8''Ⅰ' => 'I',   'Ⅱ' => 'II',  'Ⅲ' => 'III',
485         
'Ⅳ' => 'IV',  'Ⅴ' => 'V',   'Ⅵ' => 'Vi',  'Ⅶ' => 'VII''Ⅷ' => 'VIII',
486         
'Ⅸ' => 'IX',  'Ⅹ' => 'X',   'Ⅺ' => 'XI',  'Ⅻ' => 'XII''Ⅼ' => 'L',
487         
'Ⅽ' => 'C',   'Ⅾ' => 'D',   'Ⅿ' => 'M',   'ⅰ' => 'i',   'ⅱ' => 'ii',
488         
'ⅲ' => 'iii''ⅳ' => 'iv',  'ⅴ' => 'v',   'ⅵ' => 'vi',  'ⅶ' => 'vii',
489         
'ⅷ' => 'viii','ⅸ' => 'ix',  'ⅹ' => 'x',   'ⅺ' => 'xi',  'ⅻ' => 'xii',
490         
'ⅼ' => 'l',   'ⅽ' => 'c',   'ⅾ' => 'd',   'ⅿ' => 'm'
491     
);
492     
493     
/**
494      * If the [http://php.net/mbstring mbstring] extension is available
495      * 
496      * @var boolean
497      */
498     
private static $_mbstring_available NULL;
499     
500     
/**
501      * Forces use as a static class
502      * 
503      * @return UTF8
504      */
505     
private function __construct() { }
506     
507     
/**
508      * Maps UTF-8 ASCII-based latin characters, puntuation, symbols and number forms to ASCII
509      * 
510      * Any characters or symbols that can not be translated will be removed.
511      * 
512      * This function is most useful for situation that only allows ASCII, such
513      * as in URLs.
514      * 
515      * Translates elements form the following unicode blocks:
516      * 
517      *  - Latin-1 Supplement
518      *  - Latin Extended-A
519      *  - Latin Extended-B
520      *  - IPA Extensions
521      *  - Latin Extended Additional
522      *  - General Punctuation
523      *  - Letterlike symbols
524      *  - Number Forms
525      * 
526      * @internal
527      * 
528      * @param  string $str  The string to convert
529      * @return string  The input string in pure ASCII
530      */
531     
public static function ascii($str) {
532         if ( ! 
self::detect($str)) {
533             return 
$str;
534         }
535         
536         
$str strtr($strself::$_utf8_to_ascii);
537         return 
preg_replace('#[^\x00-\x7F]#'''$str);
538     }
539     
540     
541     
/**
542      * Checks to see if the [http://php.net/mbstring mbstring] extension is available
543      * 
544      * @return void
545      */
546     
private static function _check_mb_string() {
547         
self::$_mbstring_available extension_loaded('mbstring');
548     }
549     
550     
551     
/**
552      * Converts a unicode value into a UTF-8 character
553      * 
554      * @param  mixed $unicode_code_point  The character to create, either the `U+hex` or decimal code point
555      * @return string  The UTF-8 character
556      */
557     
public static function chr($unicode_code_point) {
558         if (
is_string($unicode_code_point) && substr($unicode_code_point02) == 'U+') {
559             
$unicode_code_point substr($unicode_code_point2);
560             
$unicode_code_point hexdec($unicode_code_point);
561         }
562         
563         
$bin decbin($unicode_code_point);
564         
$digits strlen($bin);
565         
566         
$first $second $third $fourth NULL;
567         
568         
// One byte characters
569         
if ($digits <= 7) {
570             
$first chr(bindec($bin));
571             
572         
// Two byte characters
573         
} elseif ($digits <= 11) {
574             
$first  chr(bindec('110' str_pad(substr($bin0, -6), 5'0'STR_PAD_LEFT)));
575             
$second chr(bindec('10' substr($bin, -6)));
576             
577         
// Three byte characters
578         
} elseif ($digits <= 16) {
579             
$first  chr(bindec('1110' str_pad(substr($bin0, -12), 4'0'STR_PAD_LEFT)));
580             
$second chr(bindec('10' substr($bin, -12, -6)));
581             
$third  chr(bindec('10' substr($bin, -6)));
582             
583         
// Four byte characters
584         
} elseif ($digits <= 21) {
585             
$first  chr(bindec('11110' str_pad(substr($bin0, -18), 3'0'STR_PAD_LEFT)));
586             
$second chr(bindec('10' substr($bin, -18, -12)));
587             
$third  chr(bindec('10' substr($bin, -12, -6)));
588             
$fourth chr(bindec('10' substr($bin, -6)));
589         }
590         
591         
$ord ord($first);
592         if (
$digits 21 || $ord == 0xC0 || $ord == 0xC1 || $ord 0xF4) {
593             
halt('A System Error Was Encountered'"The code point specified {$unicode_code_point} is invalid"'sys_error');
594         }
595         
596         return 
$first $second $third $fourth;
597     }
598     
599     
600     
/**
601      * Removes any invalid UTF-8 characters from a string or array of strings
602      * 
603      * @param  array|string $value  The string or array of strings to clean
604      * @return string  The cleaned string
605      */
606     
public static function clean($value) {
607         if ( ! 
is_array($value)) {
608             if (
self::$_can_ignore_invalid === NULL) {
609                 
self::$_can_ignore_invalid strtolower(ICONV_IMPL) != 'unknown';    
610             }
611             
// The iconv PHP extension provides functionality to convert text stored in another encoding to UTF-8 using a simple function call
612             
return iconv('UTF-8''UTF-8' . (self::$_can_ignore_invalid '//IGNORE' ''), (string) $value);
613         }
614         
615         
$keys array_keys($value);
616         
$num_keys sizeof($keys);
617         for (
$i=0$i $num_keys$i++) {
618             
$value[$keys[$i]] = self::clean($value[$keys[$i]]);
619         }
620         
621         return 
$value;
622     }
623     
624     
625     
/**
626      * Compares strings, with the resulting order having latin characters that are based on ASCII letters placed after the relative ASCII characters
627      * 
628      * Please note that this function sorts based on English language sorting
629      * rules only. Locale-sepcific sorting is done by
630      * [http://php.net/strcoll strcoll()], however there are technical
631      * limitations.
632      * 
633      * @param  string $str1  The first string to compare
634      * @param  string $str2  The second string to compare
635      * @return integer  < 0 if $str1 < $str2, 0 if they are equal, > 0 if $str1 > $str2
636      */
637     
public static function cmp($str1$str2) {
638         
$ascii_str1 strtr($str1self::$_utf8_to_ascii);
639         
$ascii_str2 strtr($str2self::$_utf8_to_ascii);
640         
641         
$res strcmp($ascii_str1$ascii_str2);
642         
643         
// If the ASCII representations are the same, sort by the UTF-8 representations
644         
if ($res === 0) {
645             
$res strcmp($str1$str2);
646         }
647         
648         return 
$res;
649     }
650     
651     
652     
/**
653      * Converts an offset in characters to an offset in bytes to that we can use the built-in functions for some operations
654      * 
655      * @param  string  $str  The string to base the offset on
656      * @param  integer $offset  The character offset to conver to bytes
657      * @return integer  The converted offset
658      */
659     
private static function _convert_offset_to_bytes($str$offset) {
660         if (
$offset == 0) {
661             return 
0;
662         }
663         
664         
$len strlen($str);
665         
666         
$byte_offset     0;
667         
$measured_offset 0;
668         
$sign            1;
669         
670         
// Negative offsets require us to reverse some stuff
671         
if ($offset 0) {
672             
$str    strrev($str);
673             
$sign   = -1;
674             
$offset abs($offset);
675         }
676             
677         for (
$i 0$i $len && $measured_offset $offset$i++) {
678             
$char $str[$i];
679             ++
$byte_offset;
680             if (
ord($char) < 0x80) {
681                 ++
$measured_offset;
682             } else {
683                 switch (
ord($char) & 0xF0) {
684                     case 
0xF0:
685                     case 
0xE0:
686                     case 
0xD0:
687                     case 
0xC0:
688                         ++
$measured_offset;
689                         break;
690                 }
691             }
692         }
693         
694         return 
$byte_offset $sign;
695     }
696     
697     
698     
/**
699      * Detects if a UTF-8 string contains any non-ASCII characters
700      * 
701      * @param  string $str  The string to check
702      * @return boolean  If the string contains any non-ASCII characters
703      */
704     
private static function detect($str) {
705         return (boolean) 
preg_match('#[^\x00-\x7F]#'$str);
706     }
707     
708     
709     
/**
710      * Explodes a string on a delimiter
711      * 
712      * If no delimiter is provided, the string will be exploded with each
713      * characters being an element in the array.
714      * 
715      * @param  string  $str     The string to explode
716      * @param  string  $delimiter  The string to explode on. If `NULL` or `''` this method will return one character per array index.
717      * @return array  The exploded string
718      */
719     
public static function explode($str$delimiter NULL) {
720         
// If a delimiter was passed, we just do an explode
721         
if ($delimiter || ( ! $delimiter && is_numeric($delimiter))) {
722             return 
explode($delimiter$str);
723         }
724         
725         
// If no delimiter was passed, we explode the characters into an array
726         
preg_match_all('#.|^\z#us'$str$matches);
727         return 
$matches[0];
728     }
729     
730     
731     
/**
732      * Compares strings in a case-insensitive manner, with the resulting order having characters that are based on ASCII letters placed after the relative ASCII characters
733      * 
734      * Please note that this function sorts based on English language sorting
735      * rules only. Locale-sepcific sorting is done by
736      * [http://php.net/strcoll strcoll()], however there are technical
737      * limitations.
738      * 
739      * @param  string $str1  The first string to compare
740      * @param  string $str2  The second string to compare
741      * @return integer  < 0 if $str1 < $str2, 0 if they are equal, > 0 if $str1 > $str2
742      */
743     
public static function icmp($str1$str2) {
744         
$str1 self::lower($str1);
745         
$str2 self::lower($str2);
746         
747         return 
self::cmp($str1$str2);
748     }
749     
750     
751     
/**
752      * Compares strings using a natural order algorithm in a case-insensitive manner, with the resulting order having latin characters that are based on ASCII letters placed after the relative ASCII characters
753      * 
754      * Please note that this function sorts based on English language sorting
755      * rules only. Locale-sepcific sorting is done by
756      * [http://php.net/strcoll strcoll()], however there are technical
757      * limitations.
758      * 
759      * @param  string $str1  The first string to compare
760      * @param  string $str2  The second string to compare
761      * @return integer  `< 0` if `$str1 < $str2`, `0` if they are equal, `> 0` if `$str1 > $str2`
762      */
763     
public static function inatcmp($str1$str2) {
764         
$str1 self::lower($str1);
765         
$str2 self::lower($str2);
766         
767         return 
self::natcmp($str1$str2);
768     }
769     
770     
771     
/**
772      * Finds the first position (in characters) of the search value in the string - case is ignored when doing performing a match
773      * 
774      * @param  string  $haystack  The string to search in
775      * @param  string  $needle    The string to search for. This match will be done in a case-insensitive manner.
776      * @param  integer $offset    The character position to start searching from
777      * @return mixed  The integer character position of the first occurence of the needle or `FALSE` if no match
778      */
779     
public static function ipos($haystack$needle$offset 0) {
780         
// We get better performance falling back for ASCII strings
781         
if ( ! self::detect($haystack)) {
782             return 
stripos($haystack$needle$offset);
783         }
784         
785         if (
self::$_mbstring_available === NULL) {
786             
self::_check_mb_string();
787         }
788         
789         if (
self::$_mbstring_available && function_exists('mb_stripos')) {
790             return 
mb_stripos($haystack$needle$offset'UTF-8');
791         }
792         
793         
$haystack self::lower($haystack);
794         
$needle self::lower($needle);
795         
796         return 
self::pos($haystack$needle$offset);
797     }
798     
799     
800     
/**
801      * Replaces matching parts of the string, with matches being done in a a case-insensitive manner
802      * 
803      * If `$search` and `$replace` are both arrays and `$replace` is shorter,
804      * the extra `$search` string will be replaced with an empty string. If
805      * `$search` is an array and `$replace` is a string, all `$search` values
806      * will be replaced with the string specified.
807      * 
808      * @param  string $str   The string to perform the replacements on
809      * @param  mixed  $search   The string (or array of strings) to search for - see method description for details
810      * @param  mixed  $replace  The string (or array of strings) to replace with - see method description for details
811      * @return string  The input string with the specified replacements
812      */
813     
public static function ireplace($str$search$replace) {
814         if (
is_array($search)) {
815             foreach (
$search as &$needle) {
816                 
$needle '#' preg_quote($needle'#') . '#ui';
817             }
818         } else {
819             
$search '#' preg_quote($search'#') . '#ui';
820         }
821         return 
preg_replace(
822             
$search,
823             
strtr($replace, array('\\' => '\\\\''$' => '\\$')),
824             
$str
825         
);
826     }
827     
828     
829     
/**
830      * Finds the last position (in characters) of the search value in the string - case is ignored when doing performing a match
831      * 
832      * @param  string  $haystack  The string to search in
833      * @param  string  $needle    The string to search for. This match will be done in a case-insensitive manner.
834      * @param  integer $offset    The character position to start searching from. A negative value will stop looking that many characters from the end of the string
835      * @return mixed  The integer character position of the last occurence of the needle or `FALSE` if no match
836      */
837     
public static function irpos($haystack$needle$offset 0) {
838         
// We get better performance falling back for ASCII strings
839         
if ( ! self::detect($haystack)) {
840             return 
strripos($haystack$needle$offset);
841         }
842         
843         if (
self::$_mbstring_available === NULL) {
844             
self::_check_mb_string();
845         }
846         
847         if (
self::$_mbstring_available && function_exists('mb_strripos')) {
848             return 
mb_strripos($haystack$needle$offset'UTF-8');
849         }
850         
851         
$haystack self::lower($haystack);
852         
$needle self::lower($needle);
853         
854         return 
self::rpos($haystack$needle$offset);
855     }
856     
857     
858     
/**
859      * Matches a string needle in the string haystack, returning a substring from the beginning of the needle to the end of the haystack
860      * 
861      * Can optionally return the part of the haystack before the needle. Matching
862      * is done in a case-insensitive manner.
863      * 
864      * @param  string  $haystack       The string to search in
865      * @param  string  $needle         The string to search for. This match will be done in a case-insensitive manner.
866      * @param  boolean $before_needle  If a substring of the haystack before the needle should be returned instead of the substring from the needle to the end of the haystack
867      * @return mixed  The specified part of the haystack, or `FALSE` if the needle was not found
868      */
869     
public static function istr($haystack$needle$before_needle FALSE) {
870         
// We get better performance falling back for ASCII strings
871         
if ($before_needle == FALSE && ! self::detect($haystack)) {
872             return 
stristr($haystack$needle);
873         }
874         
875         if (
self::$_mbstring_available === NULL) {
876             
self::_check_mb_string();
877         }
878         
879         if (
self::$_mbstring_available && function_exists('mb_stristr')) {
880             return 
mb_stristr($haystack$needle$before_needle'UTF-8');
881         }
882         
883         
$lower_haystack self::lower($haystack);
884         
$lower_needle   self::lower($needle);
885         
886         
$pos strpos($lower_haystack$lower_needle);
887         
888         if (
$before_needle) {
889             return 
substr($haystack0$pos);
890         }
891         
892         return 
substr($haystack$pos);
893     }
894     
895     
896     
/**
897      * Determines the length (in actual characters) of a string
898      * 
899      * @param  string $str  The string to measure
900      * @return integer  The number of characters/bytes in the string
901      */
902     
public static function len($str) {
903         if (
self::$_mbstring_available === NULL) {
904             
self::_check_mb_string();
905         }
906         
907         if (
self::$_mbstring_available) {
908             return 
mb_strlen($str'UTF-8');
909         }
910         
911         return 
strlen(utf8_decode($str));
912     }
913     
914     
915     
/**
916      * Converts all uppercase characters to lowercase
917      * 
918      * @param  string $str  The string to convert
919      * @return string  The input string with all uppercase characters in lowercase
920      */
921     
public static function lower($str) {
922         
// We get better performance falling back for ASCII strings
923         
if ( ! self::detect($str)) {
924             return 
strtolower($str);
925         }
926         
927         if (
self::$_mbstring_available === NULL) {
928             
self::_check_mb_string();
929         }
930         
931         if (
self::$_mbstring_available) {
932             
$string mb_strtolower($string'utf-8');
933             
// For some reason mb_strtolower misses some character
934             
return strtr($strself::$_mb_upper_to_lower_fix);
935         }
936         
937         return 
strtr($strself::$_upper_to_lower);
938     }
939     
940     
941     
/**
942      * Trims whitespace, or any specified characters, from the beginning of a string
943      * 
944      * @param  string $str    The string to trim
945      * @param  string $charlist  The characters to trim
946      * @return string  The trimmed string
947      */
948     
public static function ltrim($str$charlist NULL) {
949         if (
strlen($charlist) === 0) {
950             return 
ltrim($str);
951         }
952         
953         
$search preg_quote($charlist'#');
954         
$search str_replace('-''\-'$search);
955         
$search str_replace('\.\.''-'$search);
956         return 
preg_replace('#^[' $search ']+#Du'''$str);
957     }
958     
959     
960     
/**
961      * Compares strings using a natural order algorithm, with the resulting order having latin characters that are based on ASCII letters placed after the relative ASCII characters
962      * 
963      * Please note that this function sorts based on English language sorting
964      * rules only. Locale-sepcific sorting is done by
965      * [http://php.net/strcoll strcoll()], however there are technical
966      * limitations.
967      * 
968      * @param  string $str1  The first string to compare
969      * @param  string $str2  The second string to compare
970      * @return integer  `< 0` if `$str1 < $str2`, `0` if they are equal, `> 0` if `$str1 > $str2`
971      */
972     
public static function natcmp($str1$str2) {
973         
$ascii_str1 strtr($str1self::$_utf8_to_ascii);
974         
$ascii_str2 strtr($str2self::$_utf8_to_ascii);
975         
976         
$res strnatcmp($ascii_str1$ascii_str2);
977         
978         
// If the ASCII representations are the same, sort by the UTF-8 representations
979         
if ($res === 0) {
980             
$res strnatcmp($str1$str2);
981         }
982         
983         return 
$res;
984     }
985     
986     
987     
/**
988      * Converts a UTF-8 character to a unicode code point
989      * 
990      * @param  string $character  The character to decode
991      * @return string  The U+hex unicode code point for the character
992      */
993     
public static function ord($character) {
994         
$b array_map('ord'str_split($character));
995         
$invalid FALSE;
996         
997         switch (
strlen($character)) {
998             case 
1:
999                 if (
$b[0] > 0x7F) {
1000                     
$invalid TRUE;
1001                     break;
1002                 }
1003                 
$bin decbin($b[0]);
1004                 break;
1005             
1006             case 
2:
1007                 if (
$b[0] < 0xC2 || $b[0] > 0xDF ||
1008                     
$b[1] < 0x80 || $b[1] > 0xBF) {
1009                     
$invalid TRUE;
1010                     break;
1011                 }
1012                 
$bin substr(decbin($b[0]), 3) .
1013                         
substr(decbin($b[1]), 2);
1014                 break;
1015             
1016             case 
3:
1017                 if (
$b[0] < 0xE0 || $b[0] > 0xEF ||
1018                     
$b[1] < 0x80 || $b[1] > 0xBF ||
1019                     
$b[2] < 0x80 || $b[2] > 0xBF) {
1020                     
$invalid TRUE;
1021                     break;
1022                 }
1023                 
$bin substr(decbin($b[0]), 4) .
1024                         
substr(decbin($b[1]), 2) .
1025                         
substr(decbin($b[2]), 2);
1026                 break;
1027             
1028             case 
4:
1029                 if (
$b[0] < 0xF0 || $b[0] > 0xF4 ||
1030                     
$b[1] < 0x80 || $b[1] > 0xBF ||
1031                     
$b[2] < 0x80 || $b[2] > 0xBF ||
1032                     
$b[3] < 0x80 || $b[3] > 0xBF) {
1033                     
$invalid TRUE;
1034                     break;
1035                 }
1036                 
$bin substr(decbin($b[0]), 5) .
1037                         
substr(decbin($b[1]), 2) .
1038                         
substr(decbin($b[2]), 2) .
1039                         
substr(decbin($b[3]), 2);
1040                 break;
1041             
1042             default:
1043                 
$invalid TRUE;
1044                 break;
1045         }
1046         
1047         if (
$invalid) {
1048             
halt('A System Error Was Encountered''The UTF-8 character specified is invalid''sys_error');
1049         }
1050         
1051         
$hex strtoupper(dechex(bindec($bin)));
1052         return 
'U+' str_pad($hex4'0'STR_PAD_LEFT);
1053     }
1054     
1055     
1056     
/**
1057      * Pads a string to the number of characters specified
1058      * 
1059      * @param  string  $str      The string to pad
1060      * @param  integer $pad_length  The character length to pad the string to
1061      * @param  string  $pad_string  The string to pad the source string with
1062      * @param  string  $pad_type    The type of padding to do: `'left'`, `'right'`, `'both'`
1063      * @return string  The input string padded to the specified character width
1064      */
1065     
public static function pad($str$pad_length$pad_string ' '$pad_type 'right') {
1066         
$valid_pad_types = array('right''left''both');
1067         if ( ! 
in_array($pad_type$valid_pad_types)) {
1068             
halt('A System Error Was Encountered'"The pad type specified, {$pad_type}, is not valid. Must be one of: ".implode(', '$valid_pad_types), 'sys_error');
1069         }
1070         
1071         
// We get better performance falling back for ASCII strings
1072         
if ( ! self::detect($str) && ! self::detect($pad_string)) {
1073             static 
$type_map = array(
1074                 
'left'  => STR_PAD_LEFT,
1075                 
'right' => STR_PAD_RIGHT,
1076                 
'both'  => STR_PAD_BOTH
1077             
);
1078             return 
str_pad($str$pad_length$pad_string$type_map[$pad_type]);
1079         }
1080         
1081         
1082         
$string_length     self::len($str);
1083         
$pad_string_length self::len($pad_string);
1084         
1085         
$pad_to_length     $pad_length $string_length;
1086         
1087         if (
$pad_to_length 1) {
1088             return 
$str;
1089         }
1090         
1091         
$padded 0;
1092         
$next_side 'left';
1093         
$left_pad_string '';
1094         
$right_pad_string '';
1095         
1096         while (
$padded $pad_to_length) {
1097             
1098             
// For pad strings over 1 characters long, they may be too long to fit
1099             
if ($pad_to_length $padded $pad_string_length) {
1100                 
$pad_string self::sub($pad_string0$pad_to_length $padded);
1101             }
1102             
1103             switch ((
$pad_type !== 'both') ? $pad_type $next_side) {
1104                 case 
'right':
1105                     
$right_pad_string .= $pad_string;
1106                     
$next_side 'left';
1107                     break;
1108                     
1109                 case 
'left':
1110                     
$left_pad_string .= $pad_string;
1111                     
$next_side 'right';
1112                     break;
1113             }
1114             
1115             
$padded += $pad_string_length;
1116         }
1117         
1118         return 
$left_pad_string $str $right_pad_string;
1119     }
1120     
1121     
1122     
/**
1123      * Finds the first position (in characters) of the search value in the string
1124      * 
1125      * @param  string  $haystack  The string to search in
1126      * @param  string  $needle    The string to search for
1127      * @param  integer $offset    The character position to start searching from
1128      * @return mixed  The integer character position of the first occurence of the needle or `FALSE` if no match
1129      */
1130     
public static function pos($haystack$needle$offset 0) {
1131         if (
self::$_mbstring_available === NULL) {
1132             
self::_check_mb_string();
1133         }
1134         
1135         if (
self::$_mbstring_available) {
1136             return 
mb_strpos($haystack$needle$offset'UTF-8');
1137         }
1138         
1139         
$offset self::_convert_offset_to_bytes($haystack$offset);
1140         
1141         
$position strpos($haystack$needle$offset);
1142         
1143         if (
$position === FALSE) {
1144             return 
FALSE;
1145         }
1146         
1147         return 
strlen(utf8_decode(substr($haystack0$position)));
1148     }
1149     
1150     
1151     
/**
1152      * Replaces matching parts of the string
1153      * 
1154      * If `$search` and `$replace` are both arrays and `$replace` is shorter,
1155      * the extra `$search` string will be replaced with an empty string. If
1156      * `$search` is an array and `$replace` is a string, all `$search` values
1157      * will be replaced with the string specified.
1158      * 
1159      * @param  string $str   The string to perform the replacements on
1160      * @param  mixed  $search   The string (or array of strings) to search for - see method description for details
1161      * @param  mixed  $replace  The string (or array of strings) to replace with - see method description for details
1162      * @return string  The input string with the specified replacements
1163      */
1164     
public static function replace($str$search$replace) {
1165         return 
str_replace($search$replace$str);
1166     }
1167     
1168     
1169     
/**
1170      * Resets the configuration of the class
1171      * 
1172      * @internal
1173      * 
1174      * @return void
1175      */
1176     
public static function reset() {
1177         
self::$_mbstring_available NULL;
1178     }
1179     
1180     
1181     
/**
1182      * Reverses a string
1183      * 
1184      * @param  string $str   The string to reverse
1185      * @return string  The reversed string
1186      */
1187     
public static function rev($str) {
1188         
$output '';
1189         
$len strlen($str);
1190         
1191         static 
$char_lens = array(
1192             
0xF0 => 4,
1193             
0xE0 => 3,
1194             
0xD0 => 2,
1195             
0xC0 => 2
1196         
);
1197         
1198         
$mb_char '';
1199         for (
$i 0$i $len$i++) {
1200             
$char $str[$i];
1201             if (
ord($char) < 128) {
1202                 
$output $char $output;
1203             } else {
1204                 switch (
ord($char) & 0xF0) {
1205                     case 
0xF0:
1206                         
$output $str[$i] . $str[$i+1] . $str[$i+2] . $str[$i+3] . $output;
1207                         
$i += 3;
1208                         break;
1209                         
1210                     case 
0xE0:
1211                         
$output $str[$i] . $str[$i+1] . $str[$i+2] . $output;
1212                         
$i += 2;
1213                         break;
1214                         
1215                     case 
0xD0:
1216                     case 
0xC0:
1217                         
$output $str[$i] . $str[$i+1] . $output;
1218                         
$i += 1;
1219                         break;
1220                 }
1221             }
1222         }
1223         
1224         return 
$output;
1225     }
1226     
1227     
1228     
/**
1229      * Finds the last position (in characters) of the search value in the string
1230      * 
1231      * @param  string  $haystack  The string to search in
1232      * @param  string  $needle    The string to search for.
1233      * @param  integer $offset    The character position to start searching from. A negative value will stop looking that many characters from the end of the string
1234      * @return mixed  The integer character position of the last occurence of the needle or `FALSE` if no match
1235      */
1236     
public static function rpos($haystack$needle$offset 0) {
1237         
// We get better performance falling back for ASCII strings
1238         
if ( ! self::detect($haystack)) {
1239             return 
strrpos($haystack$needle$offset);
1240         }
1241         
1242         
// We don't even both trying mb_strrpos since this method is faster
1243         
1244         
$offset self::_convert_offset_to_bytes($haystack$offset);
1245         
1246         
$position strrpos($haystack$needle$offset);
1247         
1248         if (
$position === FALSE) {
1249             return 
FALSE;
1250         }
1251         
1252         return 
strlen(utf8_decode(substr($haystack0$position)));
1253     }
1254     
1255     
1256     
/**
1257      * Trims whitespace, or any specified characters, from the end of a string
1258      * 
1259      * @param  string $str    The string to trim
1260      * @param  string $charlist  The characters to trim
1261      * @return string  The trimmed string
1262      */
1263     
public static function rtrim($str$charlist NULL) {
1264         if (
strlen($charlist) === 0) {
1265             return 
rtrim($str);
1266         }
1267         
1268         
$search preg_quote($charlist'#');
1269         
$search str_replace('-''\-'$search);
1270         
$search str_replace('\.\.''-'$search);
1271         return 
preg_replace('#[' $search ']+$#Du'''$str);
1272     }
1273     
1274     
1275     
/**
1276      * Matches a string needle in the string haystack, returning a substring from the beginning of the needle to the end of the haystack
1277      * 
1278      * Can optionally return the part of the haystack before the needle.
1279      * 
1280      * @param  string  $haystack       The string to search in
1281      * @param  string  $needle         The string to search for
1282      * @param  boolean $before_needle  If a substring of the haystack before the needle should be returned instead of the substring from the needle to the end of the haystack
1283      * @return mixed  The specified part of the haystack, or `FALSE` if the needle was not found
1284      */
1285     
public static function str($haystack$needle$before_needle FALSE) {
1286         if (
self::$_mbstring_available === NULL) {
1287             
self::_check_mb_string();
1288         }
1289         
1290         if (
self::$_mbstring_available && function_exists('mb_strstr')) {
1291             return 
mb_strstr($haystack$needle$before_needle'UTF-8');
1292         }
1293         
1294         
$pos strpos($haystack$needle);
1295         
1296         if (
$pos === FALSE) {
1297             return 
$pos;
1298         }
1299         
1300         if (
$before_needle) {
1301             return 
substr($haystack0$pos);
1302         }
1303         
1304         return 
substr($haystack$pos);
1305     }
1306     
1307     
1308     
/**
1309      * Extracts part of a string
1310      * 
1311      * @param  string  $str  The string to extract from
1312      * @param  integer $start   The zero-based starting index to extract from. Negative values will start the extraction that many characters from the end of the string.
1313      * @param  integer $length  The length of the string to extract. If an empty value is provided, the remainder of the string will be returned.
1314      * @return mixed  The extracted subtring or `FALSE` if the start is out of bounds
1315      */
1316     
public static function sub($str$start$length NULL) {
1317         if (
self::$_mbstring_available === NULL) {
1318             
self::_check_mb_string();
1319         }
1320         
1321         if (
self::$_mbstring_available) {
1322             
$str_len mb_strlen($str'UTF-8');
1323             if (
abs($start) > $str_len) {
1324                 return 
FALSE;
1325             }
1326             if (
$length === NULL) {
1327                 if (
$start >= 0) {
1328                     
$length $str_len-$start;
1329                 } else {
1330                     
$length abs($start);
1331                 }
1332             }
1333             return 
mb_substr($str$start$length'UTF-8');
1334         }
1335         
1336         
// We get better performance falling back for ASCII strings
1337         
if ( ! self::detect($str)) {
1338             if (
$length === NULL) {
1339                 if (
$start >= 0) {
1340                     
$length strlen($str)-$start;
1341                 } else {
1342                     
$length abs($start);
1343                 }
1344             }
1345             return 
substr($str$start$length);
1346         }
1347         
1348         
1349         
// This is the slowest version
1350         
$str_len strlen(utf8_decode($str));
1351         
1352         if (
abs($start) > $str_len) {
1353             return 
FALSE;
1354         }
1355         
1356         
// Optimize looking by changing to negative start positions if the
1357         // start is in the second half of the string
1358         
if ($start $str_len/2) {
1359             
$start 0-($str_len-$start);
1360         }
1361         
1362         
// Substrings to the end of the string are pretty simple
1363         
$start self::_convert_offset_to_bytes($str$start);
1364         
$str substr($str$start);
1365         
1366         if (
$length === NULL) {
1367             return 
$str;
1368         }
1369         
1370         
$length self::_convert_offset_to_bytes($str$length);
1371         return 
substr($str0$length);
1372     }
1373     
1374     
1375     
/**
1376      * Trims whitespace, or any specified characters, from the beginning and end of a string
1377      * 
1378      * @param  string $str    The string to trim
1379      * @param  string $charlist  The characters to trim, .. indicates a range
1380      * @return string  The trimmed string
1381      */
1382     
public static function trim($str$charlist NULL) {
1383         if (
strlen($charlist) === 0) {
1384             return 
trim($str);
1385         }
1386         
1387         
$search preg_quote($charlist'#');
1388         
$search str_replace('-''\-'$search);
1389         
$search str_replace('\.\.''-'$search);
1390         return 
preg_replace('#^[' $search ']+|[' $search ']+$#Du'''$str);
1391     }
1392     
1393     
1394     
/**
1395      * Converts the first character of the string to uppercase.
1396      * 
1397      * @param  string $str  The string to process
1398      * @return string  The processed string
1399      */
1400     
public static function ucfirst($str) {
1401         return 
self::upper(self::sub($str01)) . self::sub($str1);
1402     }
1403     
1404     
1405     
/**
1406      * Converts the first character of every word to uppercase
1407      * 
1408      * Words are considered to start at the beginning of the string, or after any
1409      * whitespace character.
1410      * 
1411      * @param  string $string  The string to process
1412      * @return string  The processed string
1413      */
1414     
public static function ucwords($string) {
1415         return 
preg_replace_callback(
1416             
'#(?<=^|\s|[\x{2000}-\x{200A}]|/|-|\(|\[|\{|\||"|^\'|\s\'|‘|“)(.)#u',
1417             array(
'self''_ucwords_callback'),
1418             
$string
1419         
);
1420     }
1421     
1422     
1423     
/**
1424      * Handles converting a character to uppercase for ::ucwords()
1425      * 
1426      * @param array $match  The regex match from ::ucwords()
1427      * @return string  The uppercase character
1428      */
1429     
private static function _ucwords_callback($match) {
1430         return 
self::upper($match[1]);
1431     }
1432     
1433     
1434     
/**
1435      * Converts all lowercase characters to uppercase
1436      * 
1437      * @param  string $str  The string to convert
1438      * @return string  The input string with all lowercase characters in uppercase
1439      */
1440     
public static function upper($str) {
1441         
// We get better performance falling back for ASCII strings
1442         
if ( ! self::detect($str)) {
1443             return 
strtoupper($str);
1444         }
1445         
1446         if (
self::$_mbstring_available === NULL) {
1447             
self::_check_mb_string();
1448         }
1449         
1450         if (
self::$_mbstring_available) {
1451             
$str mb_strtoupper($str'utf-8');
1452             
// For some reason mb_strtoupper misses some character
1453             
return strtr($strself::$_mb_lower_to_upper_fix);
1454         }
1455         
1456         return 
strtr($strself::$_lower_to_upper);
1457     }
1458     
1459     
1460     
/**
1461      * Wraps a string to a specific character width
1462      * 
1463      * @param  string  $str  The string to wrap
1464      * @param  integer $width    The character width to wrap to
1465      * @param  string  $break   The string to insert as a break
1466      * @param  boolean $cut     If words longer than the character width should be split to fit
1467      * @return string  The input string with all lowercase characters in uppercase
1468      */
1469     
public static function wordwrap($str$width 75$break "\n"$cut FALSE) {
1470         
// We get better performance falling back for ASCII strings
1471         
if ( ! self::detect($str)) {
1472             return 
wordwrap($str$width$break$cut);
1473         }
1474         
1475         
$words preg_split('#(?<=\s|[\x{2000}-\x{200A}])#ue'$str);
1476         
1477         
$output '';
1478         
1479         
$line_len 0;
1480         foreach (
$words as $word) {
1481             
$word_len self::len($word);
1482             
1483             
// Shorten up words that are too long
1484             
while ($cut && $word_len $width) {
1485                 
$output  .= $break;
1486                 
$output  .= self::sub($word0$width);
1487                 
$line_len $width;
1488                 
$word      self::sub($word$width);
1489                 
$word_len self::len($word);
1490             }
1491             
1492             if (
$line_len && $line_len $word_len $width) {
1493                 
$output  .= $break;
1494                 
$line_len 0;
1495             }
1496             
$output   .= $word;
1497             
$line_len += $word_len;
1498         }
1499         
1500         return 
$output;
1501     }
1502     
1503 }
1504
1505
// End of file: ./system/core/utf8.php 

Page URI: http://www.infopotato.com/index.php/code/core/utf8/