1<?php 2/** 3 * Multibyte handling methods. 4 * 5 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org) 6 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) 7 * 8 * Licensed under The MIT License 9 * For full copyright and license information, please see the LICENSE.txt 10 * Redistributions of files must retain the above copyright notice. 11 * 12 * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) 13 * @link https://cakephp.org CakePHP(tm) Project 14 * @package Cake.I18n 15 * @since CakePHP(tm) v 1.2.0.6833 16 * @license https://opensource.org/licenses/mit-license.php MIT License 17 */ 18 19/** 20 * Multibyte handling methods. 21 * 22 * @package Cake.I18n 23 */ 24class Multibyte { 25 26/** 27 * Holds the case folding values 28 * 29 * @var array 30 */ 31 protected static $_caseFold = array(); 32 33/** 34 * Holds an array of Unicode code point ranges 35 * 36 * @var array 37 */ 38 protected static $_codeRange = array(); 39 40/** 41 * Holds the current code point range 42 * 43 * @var string 44 */ 45 protected static $_table = null; 46 47/** 48 * Converts a multibyte character string 49 * to the decimal value of the character 50 * 51 * @param string $string String to convert. 52 * @return array 53 */ 54 public static function utf8($string) { 55 $map = array(); 56 57 $values = array(); 58 $find = 1; 59 $length = strlen($string); 60 61 for ($i = 0; $i < $length; $i++) { 62 $value = ord($string[$i]); 63 64 if ($value < 128) { 65 $map[] = $value; 66 } else { 67 if (empty($values)) { 68 $find = ($value < 224) ? 2 : 3; 69 } 70 $values[] = $value; 71 72 if (count($values) === $find) { 73 if ($find == 3) { 74 $map[] = (($values[0] % 16) * 4096) + (($values[1] % 64) * 64) + ($values[2] % 64); 75 } else { 76 $map[] = (($values[0] % 32) * 64) + ($values[1] % 64); 77 } 78 $values = array(); 79 $find = 1; 80 } 81 } 82 } 83 return $map; 84 } 85 86/** 87 * Converts the decimal value of a multibyte character string 88 * to a string 89 * 90 * @param array $array Values array. 91 * @return string 92 */ 93 public static function ascii($array) { 94 $ascii = ''; 95 96 foreach ($array as $utf8) { 97 if ($utf8 < 128) { 98 $ascii .= chr($utf8); 99 } elseif ($utf8 < 2048) { 100 $ascii .= chr(192 + (($utf8 - ($utf8 % 64)) / 64)); 101 $ascii .= chr(128 + ($utf8 % 64)); 102 } else { 103 $ascii .= chr(224 + (($utf8 - ($utf8 % 4096)) / 4096)); 104 $ascii .= chr(128 + ((($utf8 % 4096) - ($utf8 % 64)) / 64)); 105 $ascii .= chr(128 + ($utf8 % 64)); 106 } 107 } 108 return $ascii; 109 } 110 111/** 112 * Find position of first occurrence of a case-insensitive string. 113 * 114 * @param string $haystack The string from which to get the position of the first occurrence of $needle. 115 * @param string $needle The string to find in $haystack. 116 * @param int $offset The position in $haystack to start searching. 117 * @return int|bool The numeric position of the first occurrence of $needle in the $haystack string, 118 * or false if $needle is not found. 119 */ 120 public static function stripos($haystack, $needle, $offset = 0) { 121 if (Multibyte::checkMultibyte($haystack)) { 122 $haystack = Multibyte::strtoupper($haystack); 123 $needle = Multibyte::strtoupper($needle); 124 return Multibyte::strpos($haystack, $needle, $offset); 125 } 126 return stripos($haystack, $needle, $offset); 127 } 128 129/** 130 * Finds first occurrence of a string within another, case insensitive. 131 * 132 * @param string $haystack The string from which to get the first occurrence of $needle. 133 * @param string $needle The string to find in $haystack. 134 * @param bool $part Determines which portion of $haystack this function returns. 135 * If set to true, it returns all of $haystack from the beginning to the first occurrence of $needle. 136 * If set to false, it returns all of $haystack from the first occurrence of $needle to the end, 137 * Default value is false. 138 * @return int|bool The portion of $haystack, or false if $needle is not found. 139 */ 140 public static function stristr($haystack, $needle, $part = false) { 141 $php = (PHP_VERSION < 5.3); 142 143 if (($php && $part) || Multibyte::checkMultibyte($haystack)) { 144 $check = Multibyte::strtoupper($haystack); 145 $check = Multibyte::utf8($check); 146 $found = false; 147 148 $haystack = Multibyte::utf8($haystack); 149 $haystackCount = count($haystack); 150 151 $needle = Multibyte::strtoupper($needle); 152 $needle = Multibyte::utf8($needle); 153 $needleCount = count($needle); 154 155 $parts = array(); 156 $position = 0; 157 158 while (($found === false) && ($position < $haystackCount)) { 159 if (isset($needle[0]) && $needle[0] === $check[$position]) { 160 for ($i = 1; $i < $needleCount; $i++) { 161 if ($needle[$i] !== $check[$position + $i]) { 162 break; 163 } 164 } 165 if ($i === $needleCount) { 166 $found = true; 167 } 168 } 169 if (!$found) { 170 $parts[] = $haystack[$position]; 171 unset($haystack[$position]); 172 } 173 $position++; 174 } 175 176 if ($found && $part && !empty($parts)) { 177 return Multibyte::ascii($parts); 178 } elseif ($found && !empty($haystack)) { 179 return Multibyte::ascii($haystack); 180 } 181 return false; 182 } 183 184 if (!$php) { 185 return stristr($haystack, $needle, $part); 186 } 187 return stristr($haystack, $needle); 188 } 189 190/** 191 * Get string length. 192 * 193 * @param string $string The string being checked for length. 194 * @return int The number of characters in string $string 195 */ 196 public static function strlen($string) { 197 if (Multibyte::checkMultibyte($string)) { 198 $string = Multibyte::utf8($string); 199 return count($string); 200 } 201 return strlen($string); 202 } 203 204/** 205 * Find position of first occurrence of a string. 206 * 207 * @param string $haystack The string being checked. 208 * @param string $needle The position counted from the beginning of haystack. 209 * @param int $offset The search offset. If it is not specified, 0 is used. 210 * @return int|bool The numeric position of the first occurrence of $needle in the $haystack string. 211 * If $needle is not found, it returns false. 212 */ 213 public static function strpos($haystack, $needle, $offset = 0) { 214 if (Multibyte::checkMultibyte($haystack)) { 215 $found = false; 216 217 $haystack = Multibyte::utf8($haystack); 218 $haystackCount = count($haystack); 219 220 $needle = Multibyte::utf8($needle); 221 $needleCount = count($needle); 222 223 $position = $offset; 224 225 while (($found === false) && ($position < $haystackCount)) { 226 if (isset($needle[0]) && $needle[0] === $haystack[$position]) { 227 for ($i = 1; $i < $needleCount; $i++) { 228 if ($needle[$i] !== $haystack[$position + $i]) { 229 break; 230 } 231 } 232 if ($i === $needleCount) { 233 $found = true; 234 $position--; 235 } 236 } 237 $position++; 238 } 239 if ($found) { 240 return $position; 241 } 242 return false; 243 } 244 return strpos($haystack, $needle, $offset); 245 } 246 247/** 248 * Finds the last occurrence of a character in a string within another. 249 * 250 * @param string $haystack The string from which to get the last occurrence of $needle. 251 * @param string $needle The string to find in $haystack. 252 * @param bool $part Determines which portion of $haystack this function returns. 253 * If set to true, it returns all of $haystack from the beginning to the last occurrence of $needle. 254 * If set to false, it returns all of $haystack from the last occurrence of $needle to the end, 255 * Default value is false. 256 * @return string|bool The portion of $haystack. or false if $needle is not found. 257 */ 258 public static function strrchr($haystack, $needle, $part = false) { 259 $check = Multibyte::utf8($haystack); 260 $found = false; 261 262 $haystack = Multibyte::utf8($haystack); 263 $haystackCount = count($haystack); 264 265 $matches = array_count_values($check); 266 267 $needle = Multibyte::utf8($needle); 268 $needleCount = count($needle); 269 270 $parts = array(); 271 $position = 0; 272 273 while (($found === false) && ($position < $haystackCount)) { 274 if (isset($needle[0]) && $needle[0] === $check[$position]) { 275 for ($i = 1; $i < $needleCount; $i++) { 276 if ($needle[$i] !== $check[$position + $i]) { 277 if ($needle[$i] === $check[($position + $i) - 1]) { 278 $found = true; 279 } 280 unset($parts[$position - 1]); 281 $haystack = array_merge(array($haystack[$position]), $haystack); 282 break; 283 } 284 } 285 if (isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) { 286 $matches[$needle[0]] = $matches[$needle[0]] - 1; 287 } elseif ($i === $needleCount) { 288 $found = true; 289 } 290 } 291 292 if (!$found && isset($haystack[$position])) { 293 $parts[] = $haystack[$position]; 294 unset($haystack[$position]); 295 } 296 $position++; 297 } 298 299 if ($found && $part && !empty($parts)) { 300 return Multibyte::ascii($parts); 301 } elseif ($found && !empty($haystack)) { 302 return Multibyte::ascii($haystack); 303 } 304 return false; 305 } 306 307/** 308 * Finds the last occurrence of a character in a string within another, case insensitive. 309 * 310 * @param string $haystack The string from which to get the last occurrence of $needle. 311 * @param string $needle The string to find in $haystack. 312 * @param bool $part Determines which portion of $haystack this function returns. 313 * If set to true, it returns all of $haystack from the beginning to the last occurrence of $needle. 314 * If set to false, it returns all of $haystack from the last occurrence of $needle to the end, 315 * Default value is false. 316 * @return string|bool The portion of $haystack. or false if $needle is not found. 317 */ 318 public static function strrichr($haystack, $needle, $part = false) { 319 $check = Multibyte::strtoupper($haystack); 320 $check = Multibyte::utf8($check); 321 $found = false; 322 323 $haystack = Multibyte::utf8($haystack); 324 $haystackCount = count($haystack); 325 326 $matches = array_count_values($check); 327 328 $needle = Multibyte::strtoupper($needle); 329 $needle = Multibyte::utf8($needle); 330 $needleCount = count($needle); 331 332 $parts = array(); 333 $position = 0; 334 335 while (($found === false) && ($position < $haystackCount)) { 336 if (isset($needle[0]) && $needle[0] === $check[$position]) { 337 for ($i = 1; $i < $needleCount; $i++) { 338 if ($needle[$i] !== $check[$position + $i]) { 339 if ($needle[$i] === $check[($position + $i) - 1]) { 340 $found = true; 341 } 342 unset($parts[$position - 1]); 343 $haystack = array_merge(array($haystack[$position]), $haystack); 344 break; 345 } 346 } 347 if (isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) { 348 $matches[$needle[0]] = $matches[$needle[0]] - 1; 349 } elseif ($i === $needleCount) { 350 $found = true; 351 } 352 } 353 354 if (!$found && isset($haystack[$position])) { 355 $parts[] = $haystack[$position]; 356 unset($haystack[$position]); 357 } 358 $position++; 359 } 360 361 if ($found && $part && !empty($parts)) { 362 return Multibyte::ascii($parts); 363 } elseif ($found && !empty($haystack)) { 364 return Multibyte::ascii($haystack); 365 } 366 return false; 367 } 368 369/** 370 * Finds position of last occurrence of a string within another, case insensitive 371 * 372 * @param string $haystack The string from which to get the position of the last occurrence of $needle. 373 * @param string $needle The string to find in $haystack. 374 * @param int $offset The position in $haystack to start searching. 375 * @return int|bool The numeric position of the last occurrence of $needle in the $haystack string, 376 * or false if $needle is not found. 377 */ 378 public static function strripos($haystack, $needle, $offset = 0) { 379 if (Multibyte::checkMultibyte($haystack)) { 380 $found = false; 381 $haystack = Multibyte::strtoupper($haystack); 382 $haystack = Multibyte::utf8($haystack); 383 $haystackCount = count($haystack); 384 385 $matches = array_count_values($haystack); 386 387 $needle = Multibyte::strtoupper($needle); 388 $needle = Multibyte::utf8($needle); 389 $needleCount = count($needle); 390 391 $position = $offset; 392 393 while (($found === false) && ($position < $haystackCount)) { 394 if (isset($needle[0]) && $needle[0] === $haystack[$position]) { 395 for ($i = 1; $i < $needleCount; $i++) { 396 if ($needle[$i] !== $haystack[$position + $i]) { 397 if ($needle[$i] === $haystack[($position + $i) - 1]) { 398 $position--; 399 $found = true; 400 continue; 401 } 402 } 403 } 404 405 if (!$offset && isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) { 406 $matches[$needle[0]] = $matches[$needle[0]] - 1; 407 } elseif ($i === $needleCount) { 408 $found = true; 409 $position--; 410 } 411 } 412 $position++; 413 } 414 return ($found) ? $position : false; 415 } 416 return strripos($haystack, $needle, $offset); 417 } 418 419/** 420 * Find position of last occurrence of a string in a string. 421 * 422 * @param string $haystack The string being checked, for the last occurrence of $needle. 423 * @param string $needle The string to find in $haystack. 424 * @param int $offset May be specified to begin searching an arbitrary number of characters into the string. 425 * Negative values will stop searching at an arbitrary point prior to the end of the string. 426 * @return int|bool The numeric position of the last occurrence of $needle in the $haystack string. 427 * If $needle is not found, it returns false. 428 */ 429 public static function strrpos($haystack, $needle, $offset = 0) { 430 if (Multibyte::checkMultibyte($haystack)) { 431 $found = false; 432 433 $haystack = Multibyte::utf8($haystack); 434 $haystackCount = count($haystack); 435 436 $matches = array_count_values($haystack); 437 438 $needle = Multibyte::utf8($needle); 439 $needleCount = count($needle); 440 441 $position = $offset; 442 443 while (($found === false) && ($position < $haystackCount)) { 444 if (isset($needle[0]) && $needle[0] === $haystack[$position]) { 445 for ($i = 1; $i < $needleCount; $i++) { 446 if ($needle[$i] !== $haystack[$position + $i]) { 447 if ($needle[$i] === $haystack[($position + $i) - 1]) { 448 $position--; 449 $found = true; 450 continue; 451 } 452 } 453 } 454 455 if (!$offset && isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) { 456 $matches[$needle[0]] = $matches[$needle[0]] - 1; 457 } elseif ($i === $needleCount) { 458 $found = true; 459 $position--; 460 } 461 } 462 $position++; 463 } 464 return ($found) ? $position : false; 465 } 466 return strrpos($haystack, $needle, $offset); 467 } 468 469/** 470 * Finds first occurrence of a string within another 471 * 472 * @param string $haystack The string from which to get the first occurrence of $needle. 473 * @param string $needle The string to find in $haystack 474 * @param bool $part Determines which portion of $haystack this function returns. 475 * If set to true, it returns all of $haystack from the beginning to the first occurrence of $needle. 476 * If set to false, it returns all of $haystack from the first occurrence of $needle to the end, 477 * Default value is FALSE. 478 * @return string|bool The portion of $haystack, or true if $needle is not found. 479 */ 480 public static function strstr($haystack, $needle, $part = false) { 481 $php = (PHP_VERSION < 5.3); 482 483 if (($php && $part) || Multibyte::checkMultibyte($haystack)) { 484 $check = Multibyte::utf8($haystack); 485 $found = false; 486 487 $haystack = Multibyte::utf8($haystack); 488 $haystackCount = count($haystack); 489 490 $needle = Multibyte::utf8($needle); 491 $needleCount = count($needle); 492 493 $parts = array(); 494 $position = 0; 495 496 while (($found === false) && ($position < $haystackCount)) { 497 if (isset($needle[0]) && $needle[0] === $check[$position]) { 498 for ($i = 1; $i < $needleCount; $i++) { 499 if ($needle[$i] !== $check[$position + $i]) { 500 break; 501 } 502 } 503 if ($i === $needleCount) { 504 $found = true; 505 } 506 } 507 if (!$found) { 508 $parts[] = $haystack[$position]; 509 unset($haystack[$position]); 510 } 511 $position++; 512 } 513 514 if ($found && $part && !empty($parts)) { 515 return Multibyte::ascii($parts); 516 } elseif ($found && !empty($haystack)) { 517 return Multibyte::ascii($haystack); 518 } 519 return false; 520 } 521 522 if (!$php) { 523 return strstr($haystack, $needle, $part); 524 } 525 return strstr($haystack, $needle); 526 } 527 528/** 529 * Make a string lowercase 530 * 531 * @param string $string The string being lowercased. 532 * @return string with all alphabetic characters converted to lowercase. 533 */ 534 public static function strtolower($string) { 535 $utf8Map = Multibyte::utf8($string); 536 537 $length = count($utf8Map); 538 $lowerCase = array(); 539 540 for ($i = 0; $i < $length; $i++) { 541 $char = $utf8Map[$i]; 542 543 if ($char < 128) { 544 $str = strtolower(chr($char)); 545 $strlen = strlen($str); 546 for ($ii = 0; $ii < $strlen; $ii++) { 547 $lower = ord(substr($str, $ii, 1)); 548 } 549 $lowerCase[] = $lower; 550 $matched = true; 551 } else { 552 $matched = false; 553 $keys = static::_find($char, 'upper'); 554 555 if (!empty($keys)) { 556 foreach ($keys as $key => $value) { 557 if ($keys[$key]['upper'] == $char && count($keys[$key]['lower']) > 0) { 558 $lowerCase[] = $keys[$key]['lower'][0]; 559 $matched = true; 560 break 1; 561 } 562 } 563 } 564 } 565 if ($matched === false) { 566 $lowerCase[] = $char; 567 } 568 } 569 return Multibyte::ascii($lowerCase); 570 } 571 572/** 573 * Make a string uppercase 574 * 575 * @param string $string The string being uppercased. 576 * @return string with all alphabetic characters converted to uppercase. 577 */ 578 public static function strtoupper($string) { 579 $utf8Map = Multibyte::utf8($string); 580 581 $length = count($utf8Map); 582 $replaced = array(); 583 $upperCase = array(); 584 585 for ($i = 0; $i < $length; $i++) { 586 $char = $utf8Map[$i]; 587 588 if ($char < 128) { 589 $str = strtoupper(chr($char)); 590 $strlen = strlen($str); 591 for ($ii = 0; $ii < $strlen; $ii++) { 592 $upper = ord(substr($str, $ii, 1)); 593 } 594 $upperCase[] = $upper; 595 $matched = true; 596 597 } else { 598 $matched = false; 599 $keys = static::_find($char); 600 $keyCount = count($keys); 601 602 if (!empty($keys)) { 603 foreach ($keys as $key => $value) { 604 $matched = false; 605 $replace = 0; 606 if ($length > 1 && count($keys[$key]['lower']) > 1) { 607 $j = 0; 608 609 for ($ii = 0, $count = count($keys[$key]['lower']); $ii < $count; $ii++) { 610 $nextChar = $utf8Map[$i + $ii]; 611 612 if (isset($nextChar) && ($nextChar == $keys[$key]['lower'][$j + $ii])) { 613 $replace++; 614 } 615 } 616 if ($replace == $count) { 617 $upperCase[] = $keys[$key]['upper']; 618 $replaced = array_merge($replaced, array_values($keys[$key]['lower'])); 619 $matched = true; 620 break 1; 621 } 622 } elseif ($length > 1 && $keyCount > 1) { 623 $j = 0; 624 for ($ii = 1; $ii < $keyCount; $ii++) { 625 $nextChar = $utf8Map[$i + $ii - 1]; 626 627 if (in_array($nextChar, $keys[$ii]['lower'])) { 628 629 for ($jj = 0, $count = count($keys[$ii]['lower']); $jj < $count; $jj++) { 630 $nextChar = $utf8Map[$i + $jj]; 631 632 if (isset($nextChar) && ($nextChar == $keys[$ii]['lower'][$j + $jj])) { 633 $replace++; 634 } 635 } 636 if ($replace == $count) { 637 $upperCase[] = $keys[$ii]['upper']; 638 $replaced = array_merge($replaced, array_values($keys[$ii]['lower'])); 639 $matched = true; 640 break 2; 641 } 642 } 643 } 644 } 645 if ($keys[$key]['lower'][0] == $char) { 646 $upperCase[] = $keys[$key]['upper']; 647 $matched = true; 648 break 1; 649 } 650 } 651 } 652 } 653 if ($matched === false && !in_array($char, $replaced, true)) { 654 $upperCase[] = $char; 655 } 656 } 657 return Multibyte::ascii($upperCase); 658 } 659 660/** 661 * Count the number of substring occurrences 662 * 663 * @param string $haystack The string being checked. 664 * @param string $needle The string being found. 665 * @return int The number of times the $needle substring occurs in the $haystack string. 666 */ 667 public static function substrCount($haystack, $needle) { 668 $count = 0; 669 $haystack = Multibyte::utf8($haystack); 670 $haystackCount = count($haystack); 671 $matches = array_count_values($haystack); 672 $needle = Multibyte::utf8($needle); 673 $needleCount = count($needle); 674 675 if ($needleCount === 1 && isset($matches[$needle[0]])) { 676 return $matches[$needle[0]]; 677 } 678 679 for ($i = 0; $i < $haystackCount; $i++) { 680 if (isset($needle[0]) && $needle[0] === $haystack[$i]) { 681 for ($ii = 1; $ii < $needleCount; $ii++) { 682 if ($needle[$ii] === $haystack[$i + 1]) { 683 if ((isset($needle[$ii + 1]) && $haystack[$i + 2]) && $needle[$ii + 1] !== $haystack[$i + 2]) { 684 $count--; 685 } else { 686 $count++; 687 } 688 } 689 } 690 } 691 } 692 return $count; 693 } 694 695/** 696 * Get part of string 697 * 698 * @param string $string The string being checked. 699 * @param int $start The first position used in $string. 700 * @param int $length The maximum length of the returned string. 701 * @return string The portion of $string specified by the $string and $length parameters. 702 */ 703 public static function substr($string, $start, $length = null) { 704 if ($start === 0 && $length === null) { 705 return $string; 706 } 707 708 $string = Multibyte::utf8($string); 709 710 for ($i = 1; $i <= $start; $i++) { 711 unset($string[$i - 1]); 712 } 713 714 if ($length === null || count($string) < $length) { 715 return Multibyte::ascii($string); 716 } 717 $string = array_values($string); 718 719 $value = array(); 720 for ($i = 0; $i < $length; $i++) { 721 $value[] = $string[$i]; 722 } 723 return Multibyte::ascii($value); 724 } 725 726/** 727 * Prepare a string for mail transport, using the provided encoding 728 * 729 * @param string $string value to encode 730 * @param string $charset charset to use for encoding. defaults to UTF-8 731 * @param string $newline Newline string. 732 * @return string 733 */ 734 public static function mimeEncode($string, $charset = null, $newline = "\r\n") { 735 if (!Multibyte::checkMultibyte($string) && strlen($string) < 75) { 736 return $string; 737 } 738 739 if (empty($charset)) { 740 $charset = Configure::read('App.encoding'); 741 } 742 $charset = strtoupper($charset); 743 744 $start = '=?' . $charset . '?B?'; 745 $end = '?='; 746 $spacer = $end . $newline . ' ' . $start; 747 748 $length = 75 - strlen($start) - strlen($end); 749 $length = $length - ($length % 4); 750 if ($charset === 'UTF-8') { 751 $parts = array(); 752 $maxchars = floor(($length * 3) / 4); 753 $stringLength = strlen($string); 754 while ($stringLength > $maxchars) { 755 $i = (int)$maxchars; 756 $test = ord($string[$i]); 757 while ($test >= 128 && $test <= 191) { 758 $i--; 759 $test = ord($string[$i]); 760 } 761 $parts[] = base64_encode(substr($string, 0, $i)); 762 $string = substr($string, $i); 763 $stringLength = strlen($string); 764 } 765 $parts[] = base64_encode($string); 766 $string = implode($spacer, $parts); 767 } else { 768 $string = chunk_split(base64_encode($string), $length, $spacer); 769 $string = preg_replace('/' . preg_quote($spacer) . '$/', '', $string); 770 } 771 return $start . $string . $end; 772 } 773 774/** 775 * Return the Code points range for Unicode characters 776 * 777 * @param int $decimal Decimal value. 778 * @return string 779 */ 780 protected static function _codepoint($decimal) { 781 if ($decimal > 128 && $decimal < 256) { 782 $return = '0080_00ff'; // Latin-1 Supplement 783 } elseif ($decimal < 384) { 784 $return = '0100_017f'; // Latin Extended-A 785 } elseif ($decimal < 592) { 786 $return = '0180_024F'; // Latin Extended-B 787 } elseif ($decimal < 688) { 788 $return = '0250_02af'; // IPA Extensions 789 } elseif ($decimal >= 880 && $decimal < 1024) { 790 $return = '0370_03ff'; // Greek and Coptic 791 } elseif ($decimal < 1280) { 792 $return = '0400_04ff'; // Cyrillic 793 } elseif ($decimal < 1328) { 794 $return = '0500_052f'; // Cyrillic Supplement 795 } elseif ($decimal < 1424) { 796 $return = '0530_058f'; // Armenian 797 } elseif ($decimal >= 7680 && $decimal < 7936) { 798 $return = '1e00_1eff'; // Latin Extended Additional 799 } elseif ($decimal < 8192) { 800 $return = '1f00_1fff'; // Greek Extended 801 } elseif ($decimal >= 8448 && $decimal < 8528) { 802 $return = '2100_214f'; // Letterlike Symbols 803 } elseif ($decimal < 8592) { 804 $return = '2150_218f'; // Number Forms 805 } elseif ($decimal >= 9312 && $decimal < 9472) { 806 $return = '2460_24ff'; // Enclosed Alphanumerics 807 } elseif ($decimal >= 11264 && $decimal < 11360) { 808 $return = '2c00_2c5f'; // Glagolitic 809 } elseif ($decimal < 11392) { 810 $return = '2c60_2c7f'; // Latin Extended-C 811 } elseif ($decimal < 11520) { 812 $return = '2c80_2cff'; // Coptic 813 } elseif ($decimal >= 65280 && $decimal < 65520) { 814 $return = 'ff00_ffef'; // Halfwidth and Fullwidth Forms 815 } else { 816 $return = false; 817 } 818 static::$_codeRange[$decimal] = $return; 819 return $return; 820 } 821 822/** 823 * Find the related code folding values for $char 824 * 825 * @param int $char decimal value of character 826 * @param string $type Type 'lower' or 'upper'. Defaults to 'lower'. 827 * @return array 828 */ 829 protected static function _find($char, $type = 'lower') { 830 $found = array(); 831 if (!isset(static::$_codeRange[$char])) { 832 $range = static::_codepoint($char); 833 if ($range === false) { 834 return array(); 835 } 836 if (!Configure::configured('_cake_core_')) { 837 App::uses('PhpReader', 'Configure'); 838 Configure::config('_cake_core_', new PhpReader(CAKE . 'Config' . DS)); 839 } 840 Configure::load('unicode' . DS . 'casefolding' . DS . $range, '_cake_core_'); 841 static::$_caseFold[$range] = Configure::read($range); 842 Configure::delete($range); 843 } 844 845 if (!static::$_codeRange[$char]) { 846 return array(); 847 } 848 static::$_table = static::$_codeRange[$char]; 849 $count = count(static::$_caseFold[static::$_table]); 850 851 for ($i = 0; $i < $count; $i++) { 852 if ($type === 'lower' && static::$_caseFold[static::$_table][$i][$type][0] === $char) { 853 $found[] = static::$_caseFold[static::$_table][$i]; 854 } elseif ($type === 'upper' && static::$_caseFold[static::$_table][$i][$type] === $char) { 855 $found[] = static::$_caseFold[static::$_table][$i]; 856 } 857 } 858 return $found; 859 } 860 861/** 862 * Check the $string for multibyte characters 863 * 864 * @param string $string Value to test. 865 * @return bool 866 */ 867 public static function checkMultibyte($string) { 868 $length = strlen($string); 869 870 for ($i = 0; $i < $length; $i++) { 871 $value = ord(($string[$i])); 872 if ($value > 128) { 873 return true; 874 } 875 } 876 return false; 877 } 878 879} 880