PageRenderTime 41ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Cake/I18n/Multibyte.php

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