| [ Index ] |
PHP Cross Reference of YOURLS |
[Summary view] [Print] [Text view]
1 <?php 2 3 /* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <[email protected]> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12 namespace Symfony\Polyfill\Intl\Normalizer; 13 14 /** 15 * Normalizer is a PHP fallback implementation of the Normalizer class provided by the intl extension. 16 * 17 * It has been validated with Unicode 6.3 Normalization Conformance Test. 18 * See http://www.unicode.org/reports/tr15/ for detailed info about Unicode normalizations. 19 * 20 * @author Nicolas Grekas <[email protected]> 21 * 22 * @internal 23 */ 24 class Normalizer 25 { 26 public const FORM_D = \Normalizer::FORM_D; 27 public const FORM_KD = \Normalizer::FORM_KD; 28 public const FORM_C = \Normalizer::FORM_C; 29 public const FORM_KC = \Normalizer::FORM_KC; 30 public const NFD = \Normalizer::NFD; 31 public const NFKD = \Normalizer::NFKD; 32 public const NFC = \Normalizer::NFC; 33 public const NFKC = \Normalizer::NFKC; 34 35 private static $C; 36 private static $D; 37 private static $KD; 38 private static $cC; 39 private static $rawD; 40 private static $rawKD; 41 private static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4]; 42 private static $ASCII = "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"; 43 44 public static function isNormalized(string $s, int $form = self::FORM_C) 45 { 46 if (!\in_array($form, [self::NFD, self::NFKD, self::NFC, self::NFKC])) { 47 return false; 48 } 49 if (!isset($s[strspn($s, self::$ASCII)])) { 50 return true; 51 } 52 if (self::NFC == $form && preg_match('//u', $s) && !preg_match('/[^\x00-\x{2FF}]/u', $s)) { 53 return true; 54 } 55 56 return self::normalize($s, $form) === $s; 57 } 58 59 public static function getRawDecomposition(string $s, int $form = self::FORM_C) 60 { 61 if ('' === $s || !preg_match('//u', $s)) { 62 return null; 63 } 64 65 $ulen = $s[0] < "\x80" ? 1 : (self::$ulenMask[$s[0] & "\xF0"] ?? 0); 66 if (!$ulen || \strlen($s) !== $ulen) { 67 return null; 68 } 69 70 if (self::NFC !== $form && self::NFD !== $form && self::NFKC !== $form && self::NFKD !== $form) { 71 return ''; 72 } 73 74 if ($s >= "\xEA\xB0\x80" && $s <= "\xED\x9E\xA3") { 75 $u = unpack('C*', $s); 76 $j = (($u[1] - 224) << 12) + (($u[2] - 128) << 6) + $u[3] - 0xAC80; 77 78 if ($t = $j % 28) { 79 $j -= $t; 80 $lv = 0xAC00 + $j; 81 $r = \chr(0xE0 | $lv >> 12).\chr(0x80 | $lv >> 6 & 0x3F).\chr(0x80 | $lv & 0x3F); 82 $r .= $t < 25 83 ? ("\xE1\x86".\chr(0xA7 + $t)) 84 : ("\xE1\x87".\chr(0x67 + $t)); 85 86 return $r; 87 } 88 89 return "\xE1\x84".\chr(0x80 + (int) ($j / 588)) 90 ."\xE1\x85".\chr(0xA1 + (int) (($j % 588) / 28)); 91 } 92 93 if (null === self::$rawD) { 94 self::$rawD = self::getData('rawCanonicalDecomposition'); 95 } 96 97 if (isset(self::$rawD[$s])) { 98 return self::$rawD[$s]; 99 } 100 101 if (self::NFKC === $form || self::NFKD === $form) { 102 if (null === self::$rawKD) { 103 self::$rawKD = self::getData('rawCompatibilityDecomposition'); 104 } 105 106 return self::$rawKD[$s] ?? null; 107 } 108 109 return null; 110 } 111 112 public static function normalize(string $s, int $form = self::FORM_C) 113 { 114 if (!preg_match('//u', $s)) { 115 return false; 116 } 117 118 switch ($form) { 119 case self::NFC: $C = true; $K = false; break; 120 case self::NFD: $C = false; $K = false; break; 121 case self::NFKC: $C = true; $K = true; break; 122 case self::NFKD: $C = false; $K = true; break; 123 default: 124 if (\defined('Normalizer::NONE') && \Normalizer::NONE == $form) { 125 return $s; 126 } 127 128 if (80000 > \PHP_VERSION_ID) { 129 return false; 130 } 131 132 throw new \ValueError('normalizer_normalize(): Argument #2 ($form) must be a a valid normalization form'); 133 } 134 135 if ('' === $s) { 136 return ''; 137 } 138 139 if ($K && null === self::$KD) { 140 self::$KD = self::getData('compatibilityDecomposition'); 141 } 142 143 if (null === self::$D) { 144 self::$D = self::getData('canonicalDecomposition'); 145 self::$cC = self::getData('combiningClass'); 146 } 147 148 if (null !== $mbEncoding = (2 /* MB_OVERLOAD_STRING */ & (int) \ini_get('mbstring.func_overload')) ? mb_internal_encoding() : null) { 149 mb_internal_encoding('8bit'); 150 } 151 152 $r = self::decompose($s, $K); 153 154 if ($C) { 155 if (null === self::$C) { 156 self::$C = self::getData('canonicalComposition'); 157 } 158 159 $r = self::recompose($r); 160 } 161 if (null !== $mbEncoding) { 162 mb_internal_encoding($mbEncoding); 163 } 164 165 return $r; 166 } 167 168 private static function recompose($s) 169 { 170 $ASCII = self::$ASCII; 171 $compMap = self::$C; 172 $combClass = self::$cC; 173 $ulenMask = self::$ulenMask; 174 175 $result = $tail = ''; 176 177 $i = $s[0] < "\x80" ? 1 : $ulenMask[$s[0] & "\xF0"]; 178 $len = \strlen($s); 179 180 $lastUchr = substr($s, 0, $i); 181 $lastUcls = isset($combClass[$lastUchr]) ? 256 : 0; 182 183 while ($i < $len) { 184 if ($s[$i] < "\x80") { 185 // ASCII chars 186 187 if ($tail) { 188 $lastUchr .= $tail; 189 $tail = ''; 190 } 191 192 if ($j = strspn($s, $ASCII, $i + 1)) { 193 $lastUchr .= substr($s, $i, $j); 194 $i += $j; 195 } 196 197 $result .= $lastUchr; 198 $lastUchr = $s[$i]; 199 $lastUcls = 0; 200 ++$i; 201 continue; 202 } 203 204 $ulen = $ulenMask[$s[$i] & "\xF0"]; 205 $uchr = substr($s, $i, $ulen); 206 207 if ($lastUchr < "\xE1\x84\x80" || "\xE1\x84\x92" < $lastUchr 208 || $uchr < "\xE1\x85\xA1" || "\xE1\x85\xB5" < $uchr 209 || $lastUcls) { 210 // Table lookup and combining chars composition 211 212 $ucls = $combClass[$uchr] ?? 0; 213 214 if (isset($compMap[$lastUchr.$uchr]) && (!$lastUcls || $lastUcls < $ucls)) { 215 $lastUchr = $compMap[$lastUchr.$uchr]; 216 } elseif ($lastUcls = $ucls) { 217 $tail .= $uchr; 218 } else { 219 if ($tail) { 220 $lastUchr .= $tail; 221 $tail = ''; 222 } 223 224 $result .= $lastUchr; 225 $lastUchr = $uchr; 226 } 227 } else { 228 // Hangul chars 229 230 $L = \ord($lastUchr[2]) - 0x80; 231 $V = \ord($uchr[2]) - 0xA1; 232 $T = 0; 233 234 $uchr = substr($s, $i + $ulen, 3); 235 236 if ("\xE1\x86\xA7" <= $uchr && $uchr <= "\xE1\x87\x82") { 237 $T = \ord($uchr[2]) - 0xA7; 238 0 > $T && $T += 0x40; 239 $ulen += 3; 240 } 241 242 $L = 0xAC00 + ($L * 21 + $V) * 28 + $T; 243 $lastUchr = \chr(0xE0 | $L >> 12).\chr(0x80 | $L >> 6 & 0x3F).\chr(0x80 | $L & 0x3F); 244 } 245 246 $i += $ulen; 247 } 248 249 return $result.$lastUchr.$tail; 250 } 251 252 private static function decompose($s, $c) 253 { 254 $result = ''; 255 256 $ASCII = self::$ASCII; 257 $decompMap = self::$D; 258 $combClass = self::$cC; 259 $ulenMask = self::$ulenMask; 260 if ($c) { 261 $compatMap = self::$KD; 262 } 263 264 $c = []; 265 $i = 0; 266 $len = \strlen($s); 267 268 while ($i < $len) { 269 if ($s[$i] < "\x80") { 270 // ASCII chars 271 272 if ($c) { 273 ksort($c); 274 $result .= implode('', $c); 275 $c = []; 276 } 277 278 $j = 1 + strspn($s, $ASCII, $i + 1); 279 $result .= substr($s, $i, $j); 280 $i += $j; 281 continue; 282 } 283 284 $ulen = $ulenMask[$s[$i] & "\xF0"]; 285 $uchr = substr($s, $i, $ulen); 286 $i += $ulen; 287 288 if ($uchr < "\xEA\xB0\x80" || "\xED\x9E\xA3" < $uchr) { 289 // Table lookup 290 291 if ($uchr !== $j = $compatMap[$uchr] ?? ($decompMap[$uchr] ?? $uchr)) { 292 $uchr = $j; 293 294 $j = \strlen($uchr); 295 $ulen = $uchr[0] < "\x80" ? 1 : $ulenMask[$uchr[0] & "\xF0"]; 296 297 if ($ulen != $j) { 298 // Put trailing chars in $s 299 300 $j -= $ulen; 301 $i -= $j; 302 303 if (0 > $i) { 304 $s = str_repeat(' ', -$i).$s; 305 $len -= $i; 306 $i = 0; 307 } 308 309 while ($j--) { 310 $s[$i + $j] = $uchr[$ulen + $j]; 311 } 312 313 $uchr = substr($uchr, 0, $ulen); 314 } 315 } 316 if (isset($combClass[$uchr])) { 317 // Combining chars, for sorting 318 319 if (!isset($c[$combClass[$uchr]])) { 320 $c[$combClass[$uchr]] = ''; 321 } 322 $c[$combClass[$uchr]] .= $uchr; 323 continue; 324 } 325 } else { 326 // Hangul chars 327 328 $uchr = unpack('C*', $uchr); 329 $j = (($uchr[1] - 224) << 12) + (($uchr[2] - 128) << 6) + $uchr[3] - 0xAC80; 330 331 $uchr = "\xE1\x84".\chr(0x80 + (int) ($j / 588)) 332 ."\xE1\x85".\chr(0xA1 + (int) (($j % 588) / 28)); 333 334 if ($j %= 28) { 335 $uchr .= $j < 25 336 ? ("\xE1\x86".\chr(0xA7 + $j)) 337 : ("\xE1\x87".\chr(0x67 + $j)); 338 } 339 } 340 if ($c) { 341 ksort($c); 342 $result .= implode('', $c); 343 $c = []; 344 } 345 346 $result .= $uchr; 347 } 348 349 if ($c) { 350 ksort($c); 351 $result .= implode('', $c); 352 } 353 354 return $result; 355 } 356 357 private static function getData($file) 358 { 359 if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) { 360 return require $file; 361 } 362 363 return false; 364 } 365 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Sun Jul 12 05:10:03 2026 | Cross-referenced by PHPXref 0.7.1 |