PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.12.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.12.1
5.12.1 5.12.0 5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / vendor / symfony / polyfill-iconv / Iconv.php
matomo / app / vendor / symfony / polyfill-iconv Last commit date
Resources 2 years ago Iconv.php 3 months ago LICENSE 2 years ago README.md 4 years ago bootstrap.php 8 months ago bootstrap80.php 8 months ago
Iconv.php
518 lines
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11 namespace Symfony\Polyfill\Iconv;
12
13 /**
14 * iconv implementation in pure PHP, UTF-8 centric.
15 *
16 * Implemented:
17 * - iconv - Convert string to requested character encoding
18 * - iconv_mime_decode - Decodes a MIME header field
19 * - iconv_mime_decode_headers - Decodes multiple MIME header fields at once
20 * - iconv_get_encoding - Retrieve internal configuration variables of iconv extension
21 * - iconv_set_encoding - Set current setting for character encoding conversion
22 * - iconv_mime_encode - Composes a MIME header field
23 * - iconv_strlen - Returns the character count of string
24 * - iconv_strpos - Finds position of first occurrence of a needle within a haystack
25 * - iconv_strrpos - Finds the last occurrence of a needle within a haystack
26 * - iconv_substr - Cut out part of a string
27 *
28 * Charsets available for conversion are defined by files
29 * in the charset/ directory and by Iconv::$alias below.
30 * You're welcome to send back any addition you make.
31 *
32 * @author Nicolas Grekas <p@tchwork.com>
33 *
34 * @internal
35 */
36 final class Iconv
37 {
38 public const ERROR_ILLEGAL_CHARACTER = 'iconv(): Detected an illegal character in input string';
39 public const ERROR_WRONG_CHARSET = 'iconv(): Wrong charset, conversion from `%s\' to `%s\' is not allowed';
40 public static $inputEncoding = 'utf-8';
41 public static $outputEncoding = 'utf-8';
42 public static $internalEncoding = 'utf-8';
43 private static $alias = ['utf8' => 'utf-8', 'ascii' => 'us-ascii', 'tis-620' => 'iso-8859-11', 'cp1250' => 'windows-1250', 'cp1251' => 'windows-1251', 'cp1252' => 'windows-1252', 'cp1253' => 'windows-1253', 'cp1254' => 'windows-1254', 'cp1255' => 'windows-1255', 'cp1256' => 'windows-1256', 'cp1257' => 'windows-1257', 'cp1258' => 'windows-1258', 'shift-jis' => 'cp932', 'shift_jis' => 'cp932', 'latin1' => 'iso-8859-1', 'latin2' => 'iso-8859-2', 'latin3' => 'iso-8859-3', 'latin4' => 'iso-8859-4', 'latin5' => 'iso-8859-9', 'latin6' => 'iso-8859-10', 'latin7' => 'iso-8859-13', 'latin8' => 'iso-8859-14', 'latin9' => 'iso-8859-15', 'latin10' => 'iso-8859-16', 'iso8859-1' => 'iso-8859-1', 'iso8859-2' => 'iso-8859-2', 'iso8859-3' => 'iso-8859-3', 'iso8859-4' => 'iso-8859-4', 'iso8859-5' => 'iso-8859-5', 'iso8859-6' => 'iso-8859-6', 'iso8859-7' => 'iso-8859-7', 'iso8859-8' => 'iso-8859-8', 'iso8859-9' => 'iso-8859-9', 'iso8859-10' => 'iso-8859-10', 'iso8859-11' => 'iso-8859-11', 'iso8859-12' => 'iso-8859-12', 'iso8859-13' => 'iso-8859-13', 'iso8859-14' => 'iso-8859-14', 'iso8859-15' => 'iso-8859-15', 'iso8859-16' => 'iso-8859-16', 'iso_8859-1' => 'iso-8859-1', 'iso_8859-2' => 'iso-8859-2', 'iso_8859-3' => 'iso-8859-3', 'iso_8859-4' => 'iso-8859-4', 'iso_8859-5' => 'iso-8859-5', 'iso_8859-6' => 'iso-8859-6', 'iso_8859-7' => 'iso-8859-7', 'iso_8859-8' => 'iso-8859-8', 'iso_8859-9' => 'iso-8859-9', 'iso_8859-10' => 'iso-8859-10', 'iso_8859-11' => 'iso-8859-11', 'iso_8859-12' => 'iso-8859-12', 'iso_8859-13' => 'iso-8859-13', 'iso_8859-14' => 'iso-8859-14', 'iso_8859-15' => 'iso-8859-15', 'iso_8859-16' => 'iso-8859-16', 'iso88591' => 'iso-8859-1', 'iso88592' => 'iso-8859-2', 'iso88593' => 'iso-8859-3', 'iso88594' => 'iso-8859-4', 'iso88595' => 'iso-8859-5', 'iso88596' => 'iso-8859-6', 'iso88597' => 'iso-8859-7', 'iso88598' => 'iso-8859-8', 'iso88599' => 'iso-8859-9', 'iso885910' => 'iso-8859-10', 'iso885911' => 'iso-8859-11', 'iso885912' => 'iso-8859-12', 'iso885913' => 'iso-8859-13', 'iso885914' => 'iso-8859-14', 'iso885915' => 'iso-8859-15', 'iso885916' => 'iso-8859-16'];
44 private static $translitMap = [];
45 private static $convertMap = [];
46 private static $errorHandler;
47 private static $lastError;
48 private static $ulenMask = ["\xc0" => 2, "\xd0" => 2, "\xe0" => 3, "\xf0" => 4];
49 private static $isValidUtf8;
50 public static function iconv($inCharset, $outCharset, $str)
51 {
52 $str = (string) $str;
53 if ('' === $str) {
54 return '';
55 }
56 // Prepare for //IGNORE and //TRANSLIT
57 $translit = $ignore = '';
58 $outCharset = strtolower($outCharset);
59 $inCharset = strtolower($inCharset);
60 if ('' === $outCharset) {
61 $outCharset = 'iso-8859-1';
62 }
63 if ('' === $inCharset) {
64 $inCharset = 'iso-8859-1';
65 }
66 do {
67 $loop = \false;
68 if ('//translit' === substr($outCharset, -10)) {
69 $loop = $translit = \true;
70 $outCharset = substr($outCharset, 0, -10);
71 }
72 if ('//ignore' === substr($outCharset, -8)) {
73 $loop = $ignore = \true;
74 $outCharset = substr($outCharset, 0, -8);
75 }
76 } while ($loop);
77 do {
78 $loop = \false;
79 if ('//translit' === substr($inCharset, -10)) {
80 $loop = \true;
81 $inCharset = substr($inCharset, 0, -10);
82 }
83 if ('//ignore' === substr($inCharset, -8)) {
84 $loop = \true;
85 $inCharset = substr($inCharset, 0, -8);
86 }
87 } while ($loop);
88 if (isset(self::$alias[$inCharset])) {
89 $inCharset = self::$alias[$inCharset];
90 }
91 if (isset(self::$alias[$outCharset])) {
92 $outCharset = self::$alias[$outCharset];
93 }
94 // Load charset maps
95 if ('utf-8' !== $inCharset && !self::loadMap('from.', $inCharset, $inMap) || 'utf-8' !== $outCharset && !self::loadMap('to.', $outCharset, $outMap)) {
96 trigger_error(\sprintf(self::ERROR_WRONG_CHARSET, $inCharset, $outCharset));
97 return \false;
98 }
99 if ('utf-8' !== $inCharset) {
100 // Convert input to UTF-8
101 $result = '';
102 if (self::mapToUtf8($result, $inMap, $str, $ignore)) {
103 $str = $result;
104 } else {
105 $str = \false;
106 }
107 self::$isValidUtf8 = \true;
108 } else {
109 self::$isValidUtf8 = preg_match('//u', $str);
110 if (!self::$isValidUtf8 && !$ignore) {
111 trigger_error(self::ERROR_ILLEGAL_CHARACTER);
112 return \false;
113 }
114 if ('utf-8' === $outCharset) {
115 // UTF-8 validation
116 $str = self::utf8ToUtf8($str, $ignore);
117 }
118 }
119 if ('utf-8' !== $outCharset && \false !== $str) {
120 // Convert output to UTF-8
121 $result = '';
122 if (self::mapFromUtf8($result, $outMap, $str, $ignore, $translit)) {
123 return $result;
124 }
125 return \false;
126 }
127 return $str;
128 }
129 public static function iconv_mime_decode_headers($str, $mode = 0, $charset = null)
130 {
131 if (null === $charset) {
132 $charset = self::$internalEncoding;
133 }
134 if (\false !== strpos($str, "\r")) {
135 $str = strtr(str_replace("\r\n", "\n", $str), "\r", "\n");
136 }
137 $str = explode("\n\n", $str, 2);
138 $headers = [];
139 $str = preg_split('/\\n(?![ \\t])/', $str[0]);
140 foreach ($str as $str) {
141 $str = self::iconv_mime_decode($str, $mode, $charset);
142 if (\false === $str) {
143 return \false;
144 }
145 $str = explode(':', $str, 2);
146 if (2 === \count($str)) {
147 if (isset($headers[$str[0]])) {
148 if (!\is_array($headers[$str[0]])) {
149 $headers[$str[0]] = [$headers[$str[0]]];
150 }
151 $headers[$str[0]][] = ltrim($str[1]);
152 } else {
153 $headers[$str[0]] = ltrim($str[1]);
154 }
155 }
156 }
157 return $headers;
158 }
159 public static function iconv_mime_decode($str, $mode = 0, $charset = null)
160 {
161 if (null === $charset) {
162 $charset = self::$internalEncoding;
163 }
164 if (\ICONV_MIME_DECODE_CONTINUE_ON_ERROR & $mode) {
165 $charset .= '//IGNORE';
166 }
167 if (\false !== strpos($str, "\r")) {
168 $str = strtr(str_replace("\r\n", "\n", $str), "\r", "\n");
169 }
170 $str = preg_split('/\\n(?![ \\t])/', rtrim($str), 2);
171 $str = preg_replace('/[ \\t]*\\n[ \\t]+/', ' ', rtrim($str[0]));
172 $str = preg_split('/=\\?([^?]+)\\?([bqBQ])\\?(.*?)\\?=/', $str, -1, \PREG_SPLIT_DELIM_CAPTURE);
173 $result = self::iconv('utf-8', $charset, $str[0]);
174 if (\false === $result) {
175 return \false;
176 }
177 $i = 1;
178 $len = \count($str);
179 while ($i < $len) {
180 $c = strtolower($str[$i]);
181 if (\ICONV_MIME_DECODE_CONTINUE_ON_ERROR & $mode && 'utf-8' !== $c && !isset(self::$alias[$c]) && !self::loadMap('from.', $c, $d)) {
182 $d = \false;
183 } elseif ('B' === strtoupper($str[$i + 1])) {
184 $d = base64_decode($str[$i + 2]);
185 } else {
186 $d = rawurldecode(strtr(str_replace('%', '%25', $str[$i + 2]), '=_', '% '));
187 }
188 if (\false !== $d) {
189 if ('' !== $d) {
190 if ('' === ($d = self::iconv($c, $charset, $d))) {
191 $str[$i + 3] = substr($str[$i + 3], 1);
192 } else {
193 $result .= $d;
194 }
195 }
196 $d = self::iconv('utf-8', $charset, $str[$i + 3]);
197 if ('' !== trim($d)) {
198 $result .= $d;
199 }
200 } elseif (\ICONV_MIME_DECODE_CONTINUE_ON_ERROR & $mode) {
201 $result .= "=?{$str[$i]}?{$str[$i + 1]}?{$str[$i + 2]}?={$str[$i + 3]}";
202 } else {
203 $result = \false;
204 break;
205 }
206 $i += 4;
207 }
208 return $result;
209 }
210 public static function iconv_get_encoding($type = 'all')
211 {
212 switch ($type) {
213 case 'input_encoding':
214 return self::$inputEncoding;
215 case 'output_encoding':
216 return self::$outputEncoding;
217 case 'internal_encoding':
218 return self::$internalEncoding;
219 }
220 return ['input_encoding' => self::$inputEncoding, 'output_encoding' => self::$outputEncoding, 'internal_encoding' => self::$internalEncoding];
221 }
222 public static function iconv_set_encoding($type, $charset)
223 {
224 switch ($type) {
225 case 'input_encoding':
226 self::$inputEncoding = $charset;
227 break;
228 case 'output_encoding':
229 self::$outputEncoding = $charset;
230 break;
231 case 'internal_encoding':
232 self::$internalEncoding = $charset;
233 break;
234 default:
235 return \false;
236 }
237 return \true;
238 }
239 public static function iconv_mime_encode($fieldName, $fieldValue, $pref = null)
240 {
241 if (!\is_array($pref)) {
242 $pref = [];
243 }
244 $pref += ['scheme' => 'B', 'input-charset' => self::$internalEncoding, 'output-charset' => self::$internalEncoding, 'line-length' => 76, 'line-break-chars' => "\r\n"];
245 if (preg_match('/[\\x80-\\xFF]/', $fieldName)) {
246 $fieldName = '';
247 }
248 $scheme = strtoupper(substr($pref['scheme'], 0, 1));
249 $in = strtolower($pref['input-charset']);
250 $out = strtolower($pref['output-charset']);
251 if ('utf-8' !== $in && \false === ($fieldValue = self::iconv($in, 'utf-8', $fieldValue))) {
252 return \false;
253 }
254 preg_match_all('/./us', $fieldValue, $chars);
255 $chars = $chars[0] ?? [];
256 $lineBreak = (int) $pref['line-length'];
257 $lineStart = "=?{$pref['output-charset']}?{$scheme}?";
258 $lineLength = \strlen($fieldName) + 2 + \strlen($lineStart) + 2;
259 $lineOffset = \strlen($lineStart) + 3;
260 $lineData = '';
261 $fieldValue = [];
262 $Q = 'Q' === $scheme;
263 foreach ($chars as $c) {
264 if ('utf-8' !== $out && \false === ($c = self::iconv('utf-8', $out, $c))) {
265 return \false;
266 }
267 $o = $Q ? $c = preg_replace_callback('/[=_\\?\\x00-\\x1F\\x80-\\xFF]/', [__CLASS__, 'qpByteCallback'], $c) : base64_encode($lineData . $c);
268 if (isset($o[$lineBreak - $lineLength])) {
269 if (!$Q) {
270 $lineData = base64_encode($lineData);
271 }
272 $fieldValue[] = $lineStart . $lineData . '?=';
273 $lineLength = $lineOffset;
274 $lineData = '';
275 }
276 $lineData .= $c;
277 $Q && ($lineLength += \strlen($c));
278 }
279 if ('' !== $lineData) {
280 if (!$Q) {
281 $lineData = base64_encode($lineData);
282 }
283 $fieldValue[] = $lineStart . $lineData . '?=';
284 }
285 return $fieldName . ': ' . implode($pref['line-break-chars'] . ' ', $fieldValue);
286 }
287 public static function iconv_strlen($s, $encoding = null)
288 {
289 if (null === $encoding) {
290 $encoding = self::$internalEncoding;
291 }
292 if (0 !== stripos($encoding, 'utf-8') && \false === ($s = self::iconv($encoding, 'utf-8', $s))) {
293 return \false;
294 }
295 $ulenMask = self::$ulenMask;
296 $i = 0;
297 $j = 0;
298 $len = \strlen($s);
299 while ($i < $len) {
300 $u = $s[$i] & "\xf0";
301 $i += $ulenMask[$u] ?? 1;
302 ++$j;
303 }
304 return $j;
305 }
306 public static function iconv_strpos($haystack, $needle, $offset = 0, $encoding = null)
307 {
308 if (null === $encoding) {
309 $encoding = self::$internalEncoding;
310 }
311 if (0 !== stripos($encoding, 'utf-8')) {
312 if (\false === ($haystack = self::iconv($encoding, 'utf-8', $haystack))) {
313 return \false;
314 }
315 if (\false === ($needle = self::iconv($encoding, 'utf-8', $needle))) {
316 return \false;
317 }
318 }
319 if ($offset = (int) $offset) {
320 $haystack = self::iconv_substr($haystack, $offset, 2147483647, 'utf-8');
321 }
322 $pos = strpos($haystack, $needle);
323 return \false === $pos ? \false : $offset + ($pos ? self::iconv_strlen(substr($haystack, 0, $pos), 'utf-8') : 0);
324 }
325 public static function iconv_strrpos($haystack, $needle, $encoding = null)
326 {
327 if (null === $encoding) {
328 $encoding = self::$internalEncoding;
329 }
330 if (0 !== stripos($encoding, 'utf-8')) {
331 if (\false === ($haystack = self::iconv($encoding, 'utf-8', $haystack))) {
332 return \false;
333 }
334 if (\false === ($needle = self::iconv($encoding, 'utf-8', $needle))) {
335 return \false;
336 }
337 }
338 $pos = isset($needle[0]) ? strrpos($haystack, $needle) : \false;
339 return \false === $pos ? \false : self::iconv_strlen($pos ? substr($haystack, 0, $pos) : $haystack, 'utf-8');
340 }
341 public static function iconv_substr($s, $start, $length = 2147483647, $encoding = null)
342 {
343 if (null === $encoding) {
344 $encoding = self::$internalEncoding;
345 }
346 if (0 !== stripos($encoding, 'utf-8')) {
347 $encoding = null;
348 } elseif (\false === ($s = self::iconv($encoding, 'utf-8', $s))) {
349 return \false;
350 }
351 $s = (string) $s;
352 $slen = self::iconv_strlen($s, 'utf-8');
353 $start = (int) $start;
354 if (0 > $start) {
355 $start += $slen;
356 }
357 if (0 > $start) {
358 if (\PHP_VERSION_ID < 80000) {
359 return \false;
360 }
361 $start = 0;
362 }
363 if ($start >= $slen) {
364 return \PHP_VERSION_ID >= 80000 ? '' : \false;
365 }
366 $rx = $slen - $start;
367 if (0 > $length) {
368 $length += $rx;
369 }
370 if (0 === $length) {
371 return '';
372 }
373 if (0 > $length) {
374 return \PHP_VERSION_ID >= 80000 ? '' : \false;
375 }
376 if ($length > $rx) {
377 $length = $rx;
378 }
379 $rx = '/^' . ($start ? self::pregOffset($start) : '') . '(' . self::pregOffset($length) . ')/u';
380 $s = preg_match($rx, $s, $s) ? $s[1] : '';
381 if (null === $encoding) {
382 return $s;
383 }
384 return self::iconv('utf-8', $encoding, $s);
385 }
386 private static function loadMap($type, $charset, &$map)
387 {
388 if (!isset(self::$convertMap[$type . $charset])) {
389 if (\false === ($map = self::getData($type . $charset))) {
390 if ('to.' === $type && self::loadMap('from.', $charset, $map)) {
391 $map = array_flip($map);
392 } else {
393 return \false;
394 }
395 }
396 self::$convertMap[$type . $charset] = $map;
397 } else {
398 $map = self::$convertMap[$type . $charset];
399 }
400 return \true;
401 }
402 private static function utf8ToUtf8($str, $ignore)
403 {
404 $ulenMask = self::$ulenMask;
405 $valid = self::$isValidUtf8;
406 $u = $str;
407 $i = $j = 0;
408 $len = \strlen($str);
409 while ($i < $len) {
410 if ($str[$i] < "\x80") {
411 $u[$j++] = $str[$i++];
412 } else {
413 $ulen = $str[$i] & "\xf0";
414 $ulen = $ulenMask[$ulen] ?? 1;
415 $uchr = substr($str, $i, $ulen);
416 if (1 === $ulen || !($valid || preg_match('/^.$/us', $uchr))) {
417 if ($ignore) {
418 ++$i;
419 continue;
420 }
421 trigger_error(self::ERROR_ILLEGAL_CHARACTER);
422 return \false;
423 }
424 $i += $ulen;
425 $u[$j++] = $uchr[0];
426 isset($uchr[1]) && 0 !== ($u[$j++] = $uchr[1]) && isset($uchr[2]) && 0 !== ($u[$j++] = $uchr[2]) && isset($uchr[3]) && 0 !== ($u[$j++] = $uchr[3]);
427 }
428 }
429 return substr($u, 0, $j);
430 }
431 private static function mapToUtf8(&$result, array $map, $str, $ignore)
432 {
433 $len = \strlen($str);
434 for ($i = 0; $i < $len; ++$i) {
435 if (isset($str[$i + 1], $map[$str[$i] . $str[$i + 1]])) {
436 $result .= $map[$str[$i] . $str[++$i]];
437 } elseif (isset($map[$str[$i]])) {
438 $result .= $map[$str[$i]];
439 } elseif (!$ignore) {
440 trigger_error(self::ERROR_ILLEGAL_CHARACTER);
441 return \false;
442 }
443 }
444 return \true;
445 }
446 private static function mapFromUtf8(&$result, array $map, $str, $ignore, $translit)
447 {
448 $ulenMask = self::$ulenMask;
449 $valid = self::$isValidUtf8;
450 if ($translit && !self::$translitMap) {
451 self::$translitMap = self::getData('translit');
452 }
453 $i = 0;
454 $len = \strlen($str);
455 while ($i < $len) {
456 if ($str[$i] < "\x80") {
457 $uchr = $str[$i++];
458 } else {
459 $ulen = $str[$i] & "\xf0";
460 $ulen = $ulenMask[$ulen] ?? 1;
461 $uchr = substr($str, $i, $ulen);
462 if ($ignore && (1 === $ulen || !($valid || preg_match('/^.$/us', $uchr)))) {
463 ++$i;
464 continue;
465 }
466 $i += $ulen;
467 }
468 if (isset($map[$uchr])) {
469 $result .= $map[$uchr];
470 } elseif ($translit) {
471 if (isset(self::$translitMap[$uchr])) {
472 $uchr = self::$translitMap[$uchr];
473 } elseif ($uchr >= "À") {
474 $uchr = \Normalizer::normalize($uchr, \Normalizer::NFD);
475 if ($uchr[0] < "\x80") {
476 $uchr = $uchr[0];
477 } elseif ($ignore) {
478 continue;
479 } else {
480 return \false;
481 }
482 } elseif ($ignore) {
483 continue;
484 } else {
485 return \false;
486 }
487 $str = $uchr . substr($str, $i);
488 $len = \strlen($str);
489 $i = 0;
490 } elseif (!$ignore) {
491 return \false;
492 }
493 }
494 return \true;
495 }
496 private static function qpByteCallback(array $m)
497 {
498 return '=' . strtoupper(dechex(\ord($m[0])));
499 }
500 private static function pregOffset($offset)
501 {
502 $rx = [];
503 $offset = (int) $offset;
504 while ($offset > 65535) {
505 $rx[] = '.{65535}';
506 $offset -= 65535;
507 }
508 return implode('', $rx) . '.{' . $offset . '}';
509 }
510 private static function getData($file)
511 {
512 if (file_exists($file = __DIR__ . '/Resources/charset/' . $file . '.php')) {
513 return require $file;
514 }
515 return \false;
516 }
517 }
518