‪TYPO3CMS  9.5
UnicodeTranscoder.php
Go to the documentation of this file.
1 <?php
17 namespace ‪Mso\IdnaConvert;
18 
20 {
21  private static ‪$mechs = ['ucs4', 'ucs4array', 'utf8', 'utf7', 'utf7imap'];
22  // unsupported yet: 'ucs4le', 'ucs4be', 'utf16', 'utf16le', 'utf16be'
23 
24  private static ‪$allow_overlong = false;
25  private static ‪$safe_mode;
26  private static ‪$safe_char;
27 
40  public static function ‪convert($data, $from, $to, ‪$safe_mode = false, ‪$safe_char = 0xFFFC)
41  {
42  self::$safe_mode = (‪$safe_mode) ? true : false;
43  self::$safe_char = (‪$safe_char) ? ‪$safe_char : 0xFFFC;
44 
45  if (self::$safe_mode) {
46  self::$allow_overlong = true;
47  }
48  if (!in_array($from, self::$mechs)) {
49  throw new \InvalidArgumentException(sprintf('Invalid input format %s', $from));
50  }
51  if (!in_array($to, self::$mechs)) {
52  throw new \InvalidArgumentException(sprintf('Invalid output format %s', $to));
53  }
54  if ($from != 'ucs4array') {
55  $methodName = $from . '_ucs4array';
56  $data = self::$methodName($data);
57  }
58  if ($to != 'ucs4array') {
59  $methodName = 'ucs4array_' . $to;
60  $data = self::$methodName($data);
61  }
62 
63  return $data;
64  }
65 
73  public static function ‪utf8_ucs4array($input)
74  {
75  $start_byte = $next_byte = 0;
76 
77  ‪$output = [];
78  $out_len = 0;
79  $inp_len = ‪self::byteLength($input);
80  $mode = 'next';
81  $test = 'none';
82  for ($k = 0; $k < $inp_len; ++$k) {
83  $v = ord($input{$k}); // Extract byte from input string
84 
85  if ($v < 128) { // We found an ASCII char - put into stirng as is
86  ‪$output[$out_len] = $v;
87  ++$out_len;
88  if ('add' == $mode) {
89  if (self::$safe_mode) {
90  ‪$output[$out_len - 2] = ‪self::$safe_char;
91  $mode = 'next';
92  } else {
93  throw new \InvalidArgumentException(sprintf('Conversion from UTF-8 to UCS-4 failed: malformed input at byte %d', $k));
94  }
95  }
96  continue;
97  }
98  if ('next' == $mode) { // Try to find the next start byte; determine the width of the Unicode char
99  $start_byte = $v;
100  $mode = 'add';
101  $test = 'range';
102  if ($v >> 5 == 6) { // &110xxxxx 10xxxxx
103  $next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left
104  $v = ($v - 192) << 6;
105  } elseif ($v >> 4 == 14) { // &1110xxxx 10xxxxxx 10xxxxxx
106  $next_byte = 1;
107  $v = ($v - 224) << 12;
108  } elseif ($v >> 3 == 30) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
109  $next_byte = 2;
110  $v = ($v - 240) << 18;
111  } elseif (self::$safe_mode) {
112  $mode = 'next';
113  ‪$output[$out_len] = ‪self::$safe_char;
114  ++$out_len;
115  continue;
116  } else {
117  throw new \InvalidArgumentException(sprintf('This might be UTF-8, but I don\'t understand it at byte %d', $k));
118  }
119  if ($inp_len - $k - $next_byte < 2) {
120  ‪$output[$out_len] = ‪self::$safe_char;
121  $mode = 'no';
122  continue;
123  }
124 
125  if ('add' == $mode) {
126  ‪$output[$out_len] = (int)$v;
127  ++$out_len;
128  continue;
129  }
130  }
131  if ('add' == $mode) {
132  if (!self::$allow_overlong && $test == 'range') {
133  $test = 'none';
134  if (($v < 0xA0 && $start_byte == 0xE0) || ($v < 0x90 && $start_byte == 0xF0) || ($v > 0x8F && $start_byte == 0xF4)) {
135  throw new \InvalidArgumentException(sprintf('Bogus UTF-8 character detected (out of legal range) at byte %d', $k));
136  }
137  }
138  if ($v >> 6 == 2) { // Bit mask must be 10xxxxxx
139  $v = ($v - 128) << ($next_byte * 6);
140  ‪$output[($out_len - 1)] += $v;
141  --$next_byte;
142  } else {
143  if (self::$safe_mode) {
144  ‪$output[$out_len - 1] = ord(self::$safe_char);
145  $k--;
146  $mode = 'next';
147  continue;
148  }
149  throw new \InvalidArgumentException(sprintf('Conversion from UTF-8 to UCS-4 failed: malformed input at byte %d', $k));
150  }
151  if ($next_byte < 0) {
152  $mode = 'next';
153  }
154  }
155  } // for
156 
157  return ‪$output;
158  }
159 
166  public static function ‪ucs4array_utf8($input)
167  {
168  ‪$output = '';
169  foreach ($input as $k => $v) {
170  if ($v < 128) { // 7bit are transferred literally
171  ‪$output .= chr($v);
172  } elseif ($v < (1 << 11)) { // 2 bytes
173  ‪$output .= chr(192 + ($v >> 6)) . chr(128 + ($v & 63));
174  } elseif ($v < (1 << 16)) { // 3 bytes
175  ‪$output .= chr(224 + ($v >> 12)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63));
176  } elseif ($v < (1 << 21)) { // 4 bytes
177  ‪$output .= chr(240 + ($v >> 18)) . chr(128 + (($v >> 12) & 63)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63));
178  } elseif (self::$safe_mode) {
180  } else {
181  throw new \InvalidArgumentException(sprintf('Conversion from UCS-4 to UTF-8 failed: malformed input at byte %d', $k));
182  }
183  }
184 
185  return ‪$output;
186  }
187 
188  public static function ‪utf7imap_ucs4array($input)
189  {
190  return ‪self::utf7_ucs4array(str_replace(',', '/', $input), '&');
191  }
192 
193  public static function ‪utf7_ucs4array($input, $sc = '+')
194  {
195  ‪$output = [];
196  $out_len = 0;
197  $inp_len = ‪self::byteLength($input);
198  $mode = 'd';
199  $b64 = '';
200 
201  for ($k = 0; $k < $inp_len; ++$k) {
202  $c = $input{$k};
203 
204  // Ignore zero bytes
205  if (0 == ord($c)) {
206  continue;
207  }
208  if ('b' == $mode) {
209  // Sequence got terminated
210  if (!preg_match('![A-Za-z0-9/' . preg_quote($sc, '!') . ']!', $c)) {
211  if ('-' == $c) {
212  if ($b64 == '') {
213  ‪$output[$out_len] = ord($sc);
214  $out_len++;
215  $mode = 'd';
216 
217  continue;
218  }
219  }
220  $tmp = base64_decode($b64);
221  $tmp = substr($tmp, -1 * (strlen($tmp) % 2));
222  for ($i = 0; $i < strlen($tmp); $i++) {
223  if ($i % 2) {
224  ‪$output[$out_len] += ord($tmp{$i});
225  $out_len++;
226  } else {
227  ‪$output[$out_len] = ord($tmp{$i}) << 8;
228  }
229  }
230  $mode = 'd';
231  $b64 = '';
232 
233  continue;
234  }
235  $b64 .= $c;
236  }
237  if ('d' == $mode) {
238  if ($sc == $c) {
239  $mode = 'b';
240 
241  continue;
242  }
243  ‪$output[$out_len] = ord($c);
244  $out_len++;
245  }
246  }
247 
248  return ‪$output;
249  }
250 
251  public static function ‪ucs4array_utf7imap($input)
252  {
253  return str_replace('/', ',', self::ucs4array_utf7($input, '&'));
254  }
255 
256  public static function ‪ucs4array_utf7($input, $sc = '+')
257  {
258  ‪$output = '';
259  $mode = 'd';
260  $b64 = '';
261  while (true) {
262  $v = (!empty($input)) ? array_shift($input) : false;
263  $is_direct = (false !== $v) ? (0x20 <= $v && $v <= 0x7e && $v != ord($sc)) : true;
264  if ($mode == 'b') {
265  if ($is_direct) {
266  if ($b64 == chr(0) . $sc) {
267  ‪$output .= $sc . '-';
268  $b64 = '';
269  } elseif ($b64) {
270  ‪$output .= $sc . str_replace('=', '', base64_encode($b64)) . '-';
271  $b64 = '';
272  }
273  $mode = 'd';
274  } elseif (false !== $v) {
275  $b64 .= chr(($v >> 8) & 255) . chr($v & 255);
276  }
277  }
278  if ($mode == 'd' && false !== $v) {
279  if ($is_direct) {
280  ‪$output .= chr($v);
281  } else {
282  $b64 = chr(($v >> 8) & 255) . chr($v & 255);
283  $mode = 'b';
284  }
285  }
286  if (false === $v && $b64 == '') {
287  break;
288  }
289  }
290 
291  return ‪$output;
292  }
293 
299  public static function ‪ucs4array_ucs4($input)
300  {
301  ‪$output = '';
302  foreach ($input as $v) {
303  ‪$output .= chr(($v >> 24) & 255) . chr(($v >> 16) & 255) . chr(($v >> 8) & 255) . chr($v & 255);
304  }
305 
306  return ‪$output;
307  }
308 
314  public static function ‪ucs4_ucs4array($input)
315  {
316  ‪$output = [];
317 
318  $inp_len = ‪self::byteLength($input);
319  // Input length must be dividable by 4
320  if ($inp_len % 4) {
321  throw new \InvalidArgumentException('Input UCS4 string is broken');
322  }
323  // Empty input - return empty output
324  if (!$inp_len) {
325  return ‪$output;
326  }
327 
328  for ($i = 0, $out_len = -1; $i < $inp_len; ++$i) {
329  if (!($i % 4)) { // Increment output position every 4 input bytes
330  $out_len++;
331  ‪$output[$out_len] = 0;
332  }
333  ‪$output[$out_len] += ord($input{$i}) << (8 * (3 - ($i % 4)));
334  }
335 
336  return ‪$output;
337  }
338 
346  protected static function ‪byteLength($string)
347  {
348  if ((extension_loaded('mbstring') && (ini_get('mbstring.func_overload') & 0x02) === 0x02)) {
349  return mb_strlen($string, '8bit');
350  }
351  return strlen((binary)$string);
352  }
353 }
‪Mso\IdnaConvert\UnicodeTranscoder\utf7_ucs4array
‪static utf7_ucs4array($input, $sc='+')
Definition: UnicodeTranscoder.php:193
‪Mso\IdnaConvert\UnicodeTranscoder\$mechs
‪static $mechs
Definition: UnicodeTranscoder.php:21
‪Mso\IdnaConvert\UnicodeTranscoder\ucs4array_utf7
‪static ucs4array_utf7($input, $sc='+')
Definition: UnicodeTranscoder.php:256
‪Mso\IdnaConvert\UnicodeTranscoder\convert
‪static mixed convert($data, $from, $to, $safe_mode=false, $safe_char=0xFFFC)
Definition: UnicodeTranscoder.php:40
‪Mso\IdnaConvert\UnicodeTranscoder\utf7imap_ucs4array
‪static utf7imap_ucs4array($input)
Definition: UnicodeTranscoder.php:188
‪Mso\IdnaConvert\UnicodeTranscoder\utf8_ucs4array
‪static array utf8_ucs4array($input)
Definition: UnicodeTranscoder.php:73
‪Mso\IdnaConvert\UnicodeTranscoder\ucs4array_utf8
‪static string ucs4array_utf8($input)
Definition: UnicodeTranscoder.php:166
‪Mso\IdnaConvert\UnicodeTranscoder\$safe_char
‪static $safe_char
Definition: UnicodeTranscoder.php:26
‪Mso\IdnaConvert\UnicodeTranscoder\ucs4_ucs4array
‪static array ucs4_ucs4array($input)
Definition: UnicodeTranscoder.php:314
‪Mso\IdnaConvert\UnicodeTranscoder\ucs4array_utf7imap
‪static ucs4array_utf7imap($input)
Definition: UnicodeTranscoder.php:251
‪Mso\IdnaConvert
Definition: EncodingHelper.php:8
‪Mso\IdnaConvert\UnicodeTranscoder\$allow_overlong
‪static $allow_overlong
Definition: UnicodeTranscoder.php:24
‪Mso\IdnaConvert\UnicodeTranscoder\ucs4array_ucs4
‪static string ucs4array_ucs4($input)
Definition: UnicodeTranscoder.php:299
‪Mso\IdnaConvert\UnicodeTranscoder\$safe_mode
‪static $safe_mode
Definition: UnicodeTranscoder.php:25
‪$output
‪$output
Definition: annotationChecker.php:113
‪Mso\IdnaConvert\UnicodeTranscoderInterface
Definition: UnicodeTranscoderInterface.php:20
‪Mso\IdnaConvert\UnicodeTranscoder\byteLength
‪static int byteLength($string)
Definition: UnicodeTranscoder.php:346
‪Mso\IdnaConvert\UnicodeTranscoder
Definition: UnicodeTranscoder.php:20