/lib/Cake/I18n/Multibyte.php

https://gitlab.com/manuperazafa/elsartenbackend · PHP · 879 lines · 590 code · 98 blank · 191 comment · 184 complexity · a5dc83f7fe18130652f248d902c9c01a MD5 · raw file

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