PageRenderTime 59ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/cake/libs/multibyte.php

https://github.com/GuillaumeLarroque/festimpro
PHP | 1173 lines | 674 code | 113 blank | 386 comment | 204 complexity | 49c669e82d2c06acc646627f3a449f29 MD5 | raw file
  1. <?php
  2. /**
  3. * Multibyte handling methods.
  4. *
  5. *
  6. * PHP versions 4 and 5
  7. *
  8. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  9. * Copyright 2005-2009, Cake Software Foundation, Inc. (http://cakefoundation.org)
  10. *
  11. * Licensed under The MIT License
  12. * Redistributions of files must retain the above copyright notice.
  13. *
  14. * @copyright Copyright 2005-2009, Cake Software Foundation, Inc. (http://cakefoundation.org)
  15. * @link http://cakephp.org CakePHP(tm) Project
  16. * @package cake
  17. * @subpackage cake.cake.libs
  18. * @since CakePHP(tm) v 1.2.0.6833
  19. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  20. */
  21. if (function_exists('mb_internal_encoding')) {
  22. $encoding = Configure::read('App.encoding');
  23. if (!empty($encoding)) {
  24. mb_internal_encoding($encoding);
  25. }
  26. }
  27. /**
  28. * Find position of first occurrence of a case-insensitive string.
  29. *
  30. * @param string $haystack The string from which to get the position of the first occurrence of $needle.
  31. * @param string $needle The string to find in $haystack.
  32. * @param integer $offset The position in $haystack to start searching.
  33. * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
  34. * @return integer|boolean The numeric position of the first occurrence of $needle in the $haystack string, or false
  35. * if $needle is not found.
  36. */
  37. if (!function_exists('mb_stripos')) {
  38. function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) {
  39. return Multibyte::stripos($haystack, $needle, $offset);
  40. }
  41. }
  42. /**
  43. * Finds first occurrence of a string within another, case insensitive.
  44. *
  45. * @param string $haystack The string from which to get the first occurrence of $needle.
  46. * @param string $needle The string to find in $haystack.
  47. * @param boolean $part Determines which portion of $haystack this function returns.
  48. * If set to true, it returns all of $haystack from the beginning to the first occurrence of $needle.
  49. * If set to false, it returns all of $haystack from the first occurrence of $needle to the end,
  50. * Default value is false.
  51. * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
  52. * @return string|boolean The portion of $haystack, or false if $needle is not found.
  53. */
  54. if (!function_exists('mb_stristr')) {
  55. function mb_stristr($haystack, $needle, $part = false, $encoding = null) {
  56. return Multibyte::stristr($haystack, $needle, $part);
  57. }
  58. }
  59. /**
  60. * Get string length.
  61. *
  62. * @param string $string The string being checked for length.
  63. * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
  64. * @return integer The number of characters in string $string having character encoding encoding.
  65. * A multi-byte character is counted as 1.
  66. */
  67. if (!function_exists('mb_strlen')) {
  68. function mb_strlen($string, $encoding = null) {
  69. return Multibyte::strlen($string);
  70. }
  71. }
  72. /**
  73. * Find position of first occurrence of a string.
  74. *
  75. * @param string $haystack The string being checked.
  76. * @param string $needle The position counted from the beginning of haystack.
  77. * @param integer $offset The search offset. If it is not specified, 0 is used.
  78. * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
  79. * @return integer|boolean The numeric position of the first occurrence of $needle in the $haystack string.
  80. * If $needle is not found, it returns false.
  81. */
  82. if (!function_exists('mb_strpos')) {
  83. function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) {
  84. return Multibyte::strpos($haystack, $needle, $offset);
  85. }
  86. }
  87. /**
  88. * Finds the last occurrence of a character in a string within another.
  89. *
  90. * @param string $haystack The string from which to get the last occurrence of $needle.
  91. * @param string $needle The string to find in $haystack.
  92. * @param boolean $part Determines which portion of $haystack this function returns.
  93. * If set to true, it returns all of $haystack from the beginning to the last occurrence of $needle.
  94. * If set to false, it returns all of $haystack from the last occurrence of $needle to the end,
  95. * Default value is false.
  96. * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
  97. * @return string|boolean The portion of $haystack. or false if $needle is not found.
  98. */
  99. if (!function_exists('mb_strrchr')) {
  100. function mb_strrchr($haystack, $needle, $part = false, $encoding = null) {
  101. return Multibyte::strrchr($haystack, $needle, $part);
  102. }
  103. }
  104. /**
  105. * Finds the last occurrence of a character in a string within another, case insensitive.
  106. *
  107. * @param string $haystack The string from which to get the last occurrence of $needle.
  108. * @param string $needle The string to find in $haystack.
  109. * @param boolean $part Determines which portion of $haystack this function returns.
  110. * If set to true, it returns all of $haystack from the beginning to the last occurrence of $needle.
  111. * If set to false, it returns all of $haystack from the last occurrence of $needle to the end,
  112. * Default value is false.
  113. * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
  114. * @return string|boolean The portion of $haystack. or false if $needle is not found.
  115. */
  116. if (!function_exists('mb_strrichr')) {
  117. function mb_strrichr($haystack, $needle, $part = false, $encoding = null) {
  118. return Multibyte::strrichr($haystack, $needle, $part);
  119. }
  120. }
  121. /**
  122. * Finds position of last occurrence of a string within another, case insensitive
  123. *
  124. * @param string $haystack The string from which to get the position of the last occurrence of $needle.
  125. * @param string $needle The string to find in $haystack.
  126. * @param integer $offset The position in $haystack to start searching.
  127. * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
  128. * @return integer|boolean The numeric position of the last occurrence of $needle in the $haystack string,
  129. * or false if $needle is not found.
  130. */
  131. if (!function_exists('mb_strripos')) {
  132. function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) {
  133. return Multibyte::strripos($haystack, $needle, $offset);
  134. }
  135. }
  136. /**
  137. * Find position of last occurrence of a string in a string.
  138. *
  139. * @param string $haystack The string being checked, for the last occurrence of $needle.
  140. * @param string $needle The string to find in $haystack.
  141. * @param integer $offset May be specified to begin searching an arbitrary number of characters into the string.
  142. * Negative values will stop searching at an arbitrary point prior to the end of the string.
  143. * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
  144. * @return integer|boolean The numeric position of the last occurrence of $needle in the $haystack string.
  145. * If $needle is not found, it returns false.
  146. */
  147. if (!function_exists('mb_strrpos')) {
  148. function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) {
  149. return Multibyte::strrpos($haystack, $needle, $offset);
  150. }
  151. }
  152. /**
  153. * Finds first occurrence of a string within another
  154. *
  155. * @param string $haystack The string from which to get the first occurrence of $needle.
  156. * @param string $needle The string to find in $haystack
  157. * @param boolean $part Determines which portion of $haystack this function returns.
  158. * If set to true, it returns all of $haystack from the beginning to the first occurrence of $needle.
  159. * If set to false, it returns all of $haystack from the first occurrence of $needle to the end,
  160. * Default value is FALSE.
  161. * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
  162. * @return string|boolean The portion of $haystack, or true if $needle is not found.
  163. */
  164. if (!function_exists('mb_strstr')) {
  165. function mb_strstr($haystack, $needle, $part = false, $encoding = null) {
  166. return Multibyte::strstr($haystack, $needle, $part);
  167. }
  168. }
  169. /**
  170. * Make a string lowercase
  171. *
  172. * @param string $string The string being lowercased.
  173. * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
  174. * @return string with all alphabetic characters converted to lowercase.
  175. */
  176. if (!function_exists('mb_strtolower')) {
  177. function mb_strtolower($string, $encoding = null) {
  178. return Multibyte::strtolower($string);
  179. }
  180. }
  181. /**
  182. * Make a string uppercase
  183. *
  184. * @param string $string The string being uppercased.
  185. * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
  186. * @return string with all alphabetic characters converted to uppercase.
  187. */
  188. if (!function_exists('mb_strtoupper')) {
  189. function mb_strtoupper($string, $encoding = null) {
  190. return Multibyte::strtoupper($string);
  191. }
  192. }
  193. /**
  194. * Count the number of substring occurrences
  195. *
  196. * @param string $haystack The string being checked.
  197. * @param string $needle The string being found.
  198. * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
  199. * @return integer The number of times the $needle substring occurs in the $haystack string.
  200. */
  201. if (!function_exists('mb_substr_count')) {
  202. function mb_substr_count($haystack, $needle, $encoding = null) {
  203. return Multibyte::substrCount($haystack, $needle);
  204. }
  205. }
  206. /**
  207. * Get part of string
  208. *
  209. * @param string $string The string being checked.
  210. * @param integer $start The first position used in $string.
  211. * @param integer $length The maximum length of the returned string.
  212. * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
  213. * @return string The portion of $string specified by the $string and $length parameters.
  214. */
  215. if (!function_exists('mb_substr')) {
  216. function mb_substr($string, $start, $length = null, $encoding = null) {
  217. return Multibyte::substr($string, $start, $length);
  218. }
  219. }
  220. /**
  221. * Encode string for MIME header
  222. *
  223. * @param string $str The string being encoded
  224. * @param string $charset specifies the name of the character set in which str is represented in.
  225. * The default value is determined by the current NLS setting (mbstring.language).
  226. * @param string $transfer_encoding specifies the scheme of MIME encoding.
  227. * It should be either "B" (Base64) or "Q" (Quoted-Printable). Falls back to "B" if not given.
  228. * @param string $linefeed specifies the EOL (end-of-line) marker with which
  229. * mb_encode_mimeheader() performs line-folding
  230. * (a ยป RFC term, the act of breaking a line longer than a certain length into multiple lines.
  231. * The length is currently hard-coded to 74 characters). Falls back to "\r\n" (CRLF) if not given.
  232. * @param integer $indent [definition unknown and appears to have no affect]
  233. * @return string A converted version of the string represented in ASCII.
  234. */
  235. if (!function_exists('mb_encode_mimeheader')) {
  236. function mb_encode_mimeheader($str, $charset = 'UTF-8', $transfer_encoding = 'B', $linefeed = "\r\n", $indent = 1) {
  237. return Multibyte::mimeEncode($str, $charset, $linefeed);
  238. }
  239. }
  240. /**
  241. * Multibyte handling methods.
  242. *
  243. *
  244. * @package cake
  245. * @subpackage cake.cake.libs
  246. */
  247. class Multibyte extends Object {
  248. /**
  249. * Holds the case folding values
  250. *
  251. * @var array
  252. * @access private
  253. */
  254. var $__caseFold = array();
  255. /**
  256. * Holds an array of Unicode code point ranges
  257. *
  258. * @var array
  259. * @access private
  260. */
  261. var $__codeRange = array();
  262. /**
  263. * Holds the current code point range
  264. *
  265. * @var string
  266. * @access private
  267. */
  268. var $__table = null;
  269. /**
  270. * Gets a reference to the Multibyte object instance
  271. *
  272. * @return object Multibyte instance
  273. * @access public
  274. * @static
  275. */
  276. function &getInstance() {
  277. static $instance = array();
  278. if (!$instance) {
  279. $instance[0] =& new Multibyte();
  280. }
  281. return $instance[0];
  282. }
  283. /**
  284. * Converts a multibyte character string
  285. * to the decimal value of the character
  286. *
  287. * @param multibyte string $string
  288. * @return array
  289. * @access public
  290. * @static
  291. */
  292. function utf8($string) {
  293. $map = array();
  294. $values = array();
  295. $find = 1;
  296. $length = strlen($string);
  297. for ($i = 0; $i < $length; $i++) {
  298. $value = ord($string[$i]);
  299. if ($value < 128) {
  300. $map[] = $value;
  301. } else {
  302. if (empty($values)) {
  303. $find = ($value < 224) ? 2 : 3;
  304. }
  305. $values[] = $value;
  306. if (count($values) === $find) {
  307. if ($find == 3) {
  308. $map[] = (($values[0] % 16) * 4096) + (($values[1] % 64) * 64) + ($values[2] % 64);
  309. } else {
  310. $map[] = (($values[0] % 32) * 64) + ($values[1] % 64);
  311. }
  312. $values = array();
  313. $find = 1;
  314. }
  315. }
  316. }
  317. return $map;
  318. }
  319. /**
  320. * Converts the decimal value of a multibyte character string
  321. * to a string
  322. *
  323. * @param array $array
  324. * @return string
  325. * @access public
  326. * @static
  327. */
  328. function ascii($array) {
  329. $ascii = '';
  330. foreach ($array as $utf8) {
  331. if ($utf8 < 128) {
  332. $ascii .= chr($utf8);
  333. } elseif ($utf8 < 2048) {
  334. $ascii .= chr(192 + (($utf8 - ($utf8 % 64)) / 64));
  335. $ascii .= chr(128 + ($utf8 % 64));
  336. } else {
  337. $ascii .= chr(224 + (($utf8 - ($utf8 % 4096)) / 4096));
  338. $ascii .= chr(128 + ((($utf8 % 4096) - ($utf8 % 64)) / 64));
  339. $ascii .= chr(128 + ($utf8 % 64));
  340. }
  341. }
  342. return $ascii;
  343. }
  344. /**
  345. * Find position of first occurrence of a case-insensitive string.
  346. *
  347. * @param multi-byte string $haystack The string from which to get the position of the first occurrence of $needle.
  348. * @param multi-byte string $needle The string to find in $haystack.
  349. * @param integer $offset The position in $haystack to start searching.
  350. * @return integer|boolean The numeric position of the first occurrence of $needle in the $haystack string,
  351. * or false if $needle is not found.
  352. * @access public
  353. * @static
  354. */
  355. function stripos($haystack, $needle, $offset = 0) {
  356. if (!PHP5 || Multibyte::checkMultibyte($haystack)) {
  357. $haystack = Multibyte::strtoupper($haystack);
  358. $needle = Multibyte::strtoupper($needle);
  359. return Multibyte::strpos($haystack, $needle, $offset);
  360. }
  361. return stripos($haystack, $needle, $offset);
  362. }
  363. /**
  364. * Finds first occurrence of a string within another, case insensitive.
  365. *
  366. * @param string $haystack The string from which to get the first occurrence of $needle.
  367. * @param string $needle The string to find in $haystack.
  368. * @param boolean $part Determines which portion of $haystack this function returns.
  369. * If set to true, it returns all of $haystack from the beginning to the first occurrence of $needle.
  370. * If set to false, it returns all of $haystack from the first occurrence of $needle to the end,
  371. * Default value is false.
  372. * @return int|boolean The portion of $haystack, or false if $needle is not found.
  373. * @access public
  374. * @static
  375. */
  376. function stristr($haystack, $needle, $part = false) {
  377. $php = (PHP_VERSION < 5.3);
  378. if (($php && $part) || Multibyte::checkMultibyte($haystack)) {
  379. $check = Multibyte::strtoupper($haystack);
  380. $check = Multibyte::utf8($check);
  381. $found = false;
  382. $haystack = Multibyte::utf8($haystack);
  383. $haystackCount = count($haystack);
  384. $needle = Multibyte::strtoupper($needle);
  385. $needle = Multibyte::utf8($needle);
  386. $needleCount = count($needle);
  387. $parts = array();
  388. $position = 0;
  389. while (($found === false) && ($position < $haystackCount)) {
  390. if (isset($needle[0]) && $needle[0] === $check[$position]) {
  391. for ($i = 1; $i < $needleCount; $i++) {
  392. if ($needle[$i] !== $check[$position + $i]) {
  393. break;
  394. }
  395. }
  396. if ($i === $needleCount) {
  397. $found = true;
  398. }
  399. }
  400. if (!$found) {
  401. $parts[] = $haystack[$position];
  402. unset($haystack[$position]);
  403. }
  404. $position++;
  405. }
  406. if ($found && $part && !empty($parts)) {
  407. return Multibyte::ascii($parts);
  408. } elseif ($found && !empty($haystack)) {
  409. return Multibyte::ascii($haystack);
  410. }
  411. return false;
  412. }
  413. if (!$php) {
  414. return stristr($haystack, $needle, $part);
  415. }
  416. return stristr($haystack, $needle);
  417. }
  418. /**
  419. * Get string length.
  420. *
  421. * @param string $string The string being checked for length.
  422. * @return integer The number of characters in string $string
  423. * @access public
  424. * @static
  425. */
  426. function strlen($string) {
  427. if (Multibyte::checkMultibyte($string)) {
  428. $string = Multibyte::utf8($string);
  429. return count($string);
  430. }
  431. return strlen($string);
  432. }
  433. /**
  434. * Find position of first occurrence of a string.
  435. *
  436. * @param string $haystack The string being checked.
  437. * @param string $needle The position counted from the beginning of haystack.
  438. * @param integer $offset The search offset. If it is not specified, 0 is used.
  439. * @return integer|boolean The numeric position of the first occurrence of $needle in the $haystack string.
  440. * If $needle is not found, it returns false.
  441. * @access public
  442. * @static
  443. */
  444. function strpos($haystack, $needle, $offset = 0) {
  445. if (Multibyte::checkMultibyte($haystack)) {
  446. $found = false;
  447. $haystack = Multibyte::utf8($haystack);
  448. $haystackCount = count($haystack);
  449. $needle = Multibyte::utf8($needle);
  450. $needleCount = count($needle);
  451. $position = $offset;
  452. while (($found === false) && ($position < $haystackCount)) {
  453. if (isset($needle[0]) && $needle[0] === $haystack[$position]) {
  454. for ($i = 1; $i < $needleCount; $i++) {
  455. if ($needle[$i] !== $haystack[$position + $i]) {
  456. break;
  457. }
  458. }
  459. if ($i === $needleCount) {
  460. $found = true;
  461. $position--;
  462. }
  463. }
  464. $position++;
  465. }
  466. if ($found) {
  467. return $position;
  468. }
  469. return false;
  470. }
  471. return strpos($haystack, $needle, $offset);
  472. }
  473. /**
  474. * Finds the last occurrence of a character in a string within another.
  475. *
  476. * @param string $haystack The string from which to get the last occurrence of $needle.
  477. * @param string $needle The string to find in $haystack.
  478. * @param boolean $part Determines which portion of $haystack this function returns.
  479. * If set to true, it returns all of $haystack from the beginning to the last occurrence of $needle.
  480. * If set to false, it returns all of $haystack from the last occurrence of $needle to the end,
  481. * Default value is false.
  482. * @return string|boolean The portion of $haystack. or false if $needle is not found.
  483. * @access public
  484. * @static
  485. */
  486. function strrchr($haystack, $needle, $part = false) {
  487. $check = Multibyte::utf8($haystack);
  488. $found = false;
  489. $haystack = Multibyte::utf8($haystack);
  490. $haystackCount = count($haystack);
  491. $matches = array_count_values($check);
  492. $needle = Multibyte::utf8($needle);
  493. $needleCount = count($needle);
  494. $parts = array();
  495. $position = 0;
  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. if ($needle[$i] === $check[($position + $i) -1]) {
  501. $found = true;
  502. }
  503. unset($parts[$position - 1]);
  504. $haystack = array_merge(array($haystack[$position]), $haystack);
  505. break;
  506. }
  507. }
  508. if (isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) {
  509. $matches[$needle[0]] = $matches[$needle[0]] - 1;
  510. } elseif ($i === $needleCount) {
  511. $found = true;
  512. }
  513. }
  514. if (!$found && isset($haystack[$position])) {
  515. $parts[] = $haystack[$position];
  516. unset($haystack[$position]);
  517. }
  518. $position++;
  519. }
  520. if ($found && $part && !empty($parts)) {
  521. return Multibyte::ascii($parts);
  522. } elseif ($found && !empty($haystack)) {
  523. return Multibyte::ascii($haystack);
  524. }
  525. return false;
  526. }
  527. /**
  528. * Finds the last occurrence of a character in a string within another, case insensitive.
  529. *
  530. * @param string $haystack The string from which to get the last occurrence of $needle.
  531. * @param string $needle The string to find in $haystack.
  532. * @param boolean $part Determines which portion of $haystack this function returns.
  533. * If set to true, it returns all of $haystack from the beginning to the last occurrence of $needle.
  534. * If set to false, it returns all of $haystack from the last occurrence of $needle to the end,
  535. * Default value is false.
  536. * @return string|boolean The portion of $haystack. or false if $needle is not found.
  537. * @access public
  538. * @static
  539. */
  540. function strrichr($haystack, $needle, $part = false) {
  541. $check = Multibyte::strtoupper($haystack);
  542. $check = Multibyte::utf8($check);
  543. $found = false;
  544. $haystack = Multibyte::utf8($haystack);
  545. $haystackCount = count($haystack);
  546. $matches = array_count_values($check);
  547. $needle = Multibyte::strtoupper($needle);
  548. $needle = Multibyte::utf8($needle);
  549. $needleCount = count($needle);
  550. $parts = array();
  551. $position = 0;
  552. while (($found === false) && ($position < $haystackCount)) {
  553. if (isset($needle[0]) && $needle[0] === $check[$position]) {
  554. for ($i = 1; $i < $needleCount; $i++) {
  555. if ($needle[$i] !== $check[$position + $i]) {
  556. if ($needle[$i] === $check[($position + $i) -1]) {
  557. $found = true;
  558. }
  559. unset($parts[$position - 1]);
  560. $haystack = array_merge(array($haystack[$position]), $haystack);
  561. break;
  562. }
  563. }
  564. if (isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) {
  565. $matches[$needle[0]] = $matches[$needle[0]] - 1;
  566. } elseif ($i === $needleCount) {
  567. $found = true;
  568. }
  569. }
  570. if (!$found && isset($haystack[$position])) {
  571. $parts[] = $haystack[$position];
  572. unset($haystack[$position]);
  573. }
  574. $position++;
  575. }
  576. if ($found && $part && !empty($parts)) {
  577. return Multibyte::ascii($parts);
  578. } elseif ($found && !empty($haystack)) {
  579. return Multibyte::ascii($haystack);
  580. }
  581. return false;
  582. }
  583. /**
  584. * Finds position of last occurrence of a string within another, case insensitive
  585. *
  586. * @param string $haystack The string from which to get the position of the last occurrence of $needle.
  587. * @param string $needle The string to find in $haystack.
  588. * @param integer $offset The position in $haystack to start searching.
  589. * @return integer|boolean The numeric position of the last occurrence of $needle in the $haystack string,
  590. * or false if $needle is not found.
  591. * @access public
  592. * @static
  593. */
  594. function strripos($haystack, $needle, $offset = 0) {
  595. if (!PHP5 || Multibyte::checkMultibyte($haystack)) {
  596. $found = false;
  597. $haystack = Multibyte::strtoupper($haystack);
  598. $haystack = Multibyte::utf8($haystack);
  599. $haystackCount = count($haystack);
  600. $matches = array_count_values($haystack);
  601. $needle = Multibyte::strtoupper($needle);
  602. $needle = Multibyte::utf8($needle);
  603. $needleCount = count($needle);
  604. $position = $offset;
  605. while (($found === false) && ($position < $haystackCount)) {
  606. if (isset($needle[0]) && $needle[0] === $haystack[$position]) {
  607. for ($i = 1; $i < $needleCount; $i++) {
  608. if ($needle[$i] !== $haystack[$position + $i]) {
  609. if ($needle[$i] === $haystack[($position + $i) -1]) {
  610. $position--;
  611. $found = true;
  612. continue;
  613. }
  614. }
  615. }
  616. if (!$offset && isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) {
  617. $matches[$needle[0]] = $matches[$needle[0]] - 1;
  618. } elseif ($i === $needleCount) {
  619. $found = true;
  620. $position--;
  621. }
  622. }
  623. $position++;
  624. }
  625. return ($found) ? $position : false;
  626. }
  627. return strripos($haystack, $needle, $offset);
  628. }
  629. /**
  630. * Find position of last occurrence of a string in a string.
  631. *
  632. * @param string $haystack The string being checked, for the last occurrence of $needle.
  633. * @param string $needle The string to find in $haystack.
  634. * @param integer $offset May be specified to begin searching an arbitrary number of characters into the string.
  635. * Negative values will stop searching at an arbitrary point prior to the end of the string.
  636. * @return integer|boolean The numeric position of the last occurrence of $needle in the $haystack string.
  637. * If $needle is not found, it returns false.
  638. * @access public
  639. * @static
  640. */
  641. function strrpos($haystack, $needle, $offset = 0) {
  642. if (!PHP5 || Multibyte::checkMultibyte($haystack)) {
  643. $found = false;
  644. $haystack = Multibyte::utf8($haystack);
  645. $haystackCount = count($haystack);
  646. $matches = array_count_values($haystack);
  647. $needle = Multibyte::utf8($needle);
  648. $needleCount = count($needle);
  649. $position = $offset;
  650. while (($found === false) && ($position < $haystackCount)) {
  651. if (isset($needle[0]) && $needle[0] === $haystack[$position]) {
  652. for ($i = 1; $i < $needleCount; $i++) {
  653. if ($needle[$i] !== $haystack[$position + $i]) {
  654. if ($needle[$i] === $haystack[($position + $i) -1]) {
  655. $position--;
  656. $found = true;
  657. continue;
  658. }
  659. }
  660. }
  661. if (!$offset && isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) {
  662. $matches[$needle[0]] = $matches[$needle[0]] - 1;
  663. } elseif ($i === $needleCount) {
  664. $found = true;
  665. $position--;
  666. }
  667. }
  668. $position++;
  669. }
  670. return ($found) ? $position : false;
  671. }
  672. return strrpos($haystack, $needle, $offset);
  673. }
  674. /**
  675. * Finds first occurrence of a string within another
  676. *
  677. * @param string $haystack The string from which to get the first occurrence of $needle.
  678. * @param string $needle The string to find in $haystack
  679. * @param boolean $part Determines which portion of $haystack this function returns.
  680. * If set to true, it returns all of $haystack from the beginning to the first occurrence of $needle.
  681. * If set to false, it returns all of $haystack from the first occurrence of $needle to the end,
  682. * Default value is FALSE.
  683. * @return string|boolean The portion of $haystack, or true if $needle is not found.
  684. * @access public
  685. * @static
  686. */
  687. function strstr($haystack, $needle, $part = false) {
  688. $php = (PHP_VERSION < 5.3);
  689. if (($php && $part) || Multibyte::checkMultibyte($haystack)) {
  690. $check = Multibyte::utf8($haystack);
  691. $found = false;
  692. $haystack = Multibyte::utf8($haystack);
  693. $haystackCount = count($haystack);
  694. $needle = Multibyte::utf8($needle);
  695. $needleCount = count($needle);
  696. $parts = array();
  697. $position = 0;
  698. while (($found === false) && ($position < $haystackCount)) {
  699. if (isset($needle[0]) && $needle[0] === $check[$position]) {
  700. for ($i = 1; $i < $needleCount; $i++) {
  701. if ($needle[$i] !== $check[$position + $i]) {
  702. break;
  703. }
  704. }
  705. if ($i === $needleCount) {
  706. $found = true;
  707. }
  708. }
  709. if (!$found) {
  710. $parts[] = $haystack[$position];
  711. unset($haystack[$position]);
  712. }
  713. $position++;
  714. }
  715. if ($found && $part && !empty($parts)) {
  716. return Multibyte::ascii($parts);
  717. } elseif ($found && !empty($haystack)) {
  718. return Multibyte::ascii($haystack);
  719. }
  720. return false;
  721. }
  722. if (!$php) {
  723. return strstr($haystack, $needle, $part);
  724. }
  725. return strstr($haystack, $needle);
  726. }
  727. /**
  728. * Make a string lowercase
  729. *
  730. * @param string $string The string being lowercased.
  731. * @return string with all alphabetic characters converted to lowercase.
  732. * @access public
  733. * @static
  734. */
  735. function strtolower($string) {
  736. $_this =& Multibyte::getInstance();
  737. $utf8Map = Multibyte::utf8($string);
  738. $length = count($utf8Map);
  739. $lowerCase = array();
  740. $matched = false;
  741. for ($i = 0 ; $i < $length; $i++) {
  742. $char = $utf8Map[$i];
  743. if ($char < 128) {
  744. $str = strtolower(chr($char));
  745. $strlen = strlen($str);
  746. for ($ii = 0 ; $ii < $strlen; $ii++) {
  747. $lower = ord(substr($str, $ii, 1));
  748. }
  749. $lowerCase[] = $lower;
  750. $matched = true;
  751. } else {
  752. $matched = false;
  753. $keys = $_this->__find($char, 'upper');
  754. if (!empty($keys)) {
  755. foreach ($keys as $key => $value) {
  756. if ($keys[$key]['upper'] == $char && count($keys[$key]['lower'][0]) === 1) {
  757. $lowerCase[] = $keys[$key]['lower'][0];
  758. $matched = true;
  759. break 1;
  760. }
  761. }
  762. }
  763. }
  764. if ($matched === false) {
  765. $lowerCase[] = $char;
  766. }
  767. }
  768. return Multibyte::ascii($lowerCase);
  769. }
  770. /**
  771. * Make a string uppercase
  772. *
  773. * @param string $string The string being uppercased.
  774. * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
  775. * @return string with all alphabetic characters converted to uppercase.
  776. * @access public
  777. * @static
  778. */
  779. function strtoupper($string) {
  780. $_this =& Multibyte::getInstance();
  781. $utf8Map = Multibyte::utf8($string);
  782. $length = count($utf8Map);
  783. $matched = false;
  784. $replaced = array();
  785. $upperCase = array();
  786. for ($i = 0 ; $i < $length; $i++) {
  787. $char = $utf8Map[$i];
  788. if ($char < 128) {
  789. $str = strtoupper(chr($char));
  790. $strlen = strlen($str);
  791. for ($ii = 0 ; $ii < $strlen; $ii++) {
  792. $upper = ord(substr($str, $ii, 1));
  793. }
  794. $upperCase[] = $upper;
  795. $matched = true;
  796. } else {
  797. $matched = false;
  798. $keys = $_this->__find($char);
  799. $keyCount = count($keys);
  800. if (!empty($keys)) {
  801. foreach ($keys as $key => $value) {
  802. $matched = false;
  803. $replace = 0;
  804. if ($length > 1 && count($keys[$key]['lower']) > 1) {
  805. $j = 0;
  806. for ($ii = 0, $count = count($keys[$key]['lower']); $ii < $count; $ii++) {
  807. $nextChar = $utf8Map[$i + $ii];
  808. if (isset($nextChar) && ($nextChar == $keys[$key]['lower'][$j + $ii])) {
  809. $replace++;
  810. }
  811. }
  812. if ($replace == $count) {
  813. $upperCase[] = $keys[$key]['upper'];
  814. $replaced = array_merge($replaced, array_values($keys[$key]['lower']));
  815. $matched = true;
  816. break 1;
  817. }
  818. } elseif ($length > 1 && $keyCount > 1) {
  819. $j = 0;
  820. for ($ii = 1; $ii < $keyCount; $ii++) {
  821. $nextChar = $utf8Map[$i + $ii - 1];
  822. if (in_array($nextChar, $keys[$ii]['lower'])) {
  823. for ($jj = 0, $count = count($keys[$ii]['lower']); $jj < $count; $jj++) {
  824. $nextChar = $utf8Map[$i + $jj];
  825. if (isset($nextChar) && ($nextChar == $keys[$ii]['lower'][$j + $jj])) {
  826. $replace++;
  827. }
  828. }
  829. if ($replace == $count) {
  830. $upperCase[] = $keys[$ii]['upper'];
  831. $replaced = array_merge($replaced, array_values($keys[$ii]['lower']));
  832. $matched = true;
  833. break 2;
  834. }
  835. }
  836. }
  837. }
  838. if ($keys[$key]['lower'][0] == $char) {
  839. $upperCase[] = $keys[$key]['upper'];
  840. $matched = true;
  841. break 1;
  842. }
  843. }
  844. }
  845. }
  846. if ($matched === false && !in_array($char, $replaced, true)) {
  847. $upperCase[] = $char;
  848. }
  849. }
  850. return Multibyte::ascii($upperCase);
  851. }
  852. /**
  853. * Count the number of substring occurrences
  854. *
  855. * @param string $haystack The string being checked.
  856. * @param string $needle The string being found.
  857. * @return integer The number of times the $needle substring occurs in the $haystack string.
  858. * @access public
  859. * @static
  860. */
  861. function substrCount($haystack, $needle) {
  862. $count = 0;
  863. $haystack = Multibyte::utf8($haystack);
  864. $haystackCount = count($haystack);
  865. $matches = array_count_values($haystack);
  866. $needle = Multibyte::utf8($needle);
  867. $needleCount = count($needle);
  868. if ($needleCount === 1 && isset($matches[$needle[0]])) {
  869. return $matches[$needle[0]];
  870. }
  871. for ($i = 0; $i < $haystackCount; $i++) {
  872. if (isset($needle[0]) && $needle[0] === $haystack[$i]) {
  873. for ($ii = 1; $ii < $needleCount; $ii++) {
  874. if ($needle[$ii] === $haystack[$i + 1]) {
  875. if ((isset($needle[$ii + 1]) && $haystack[$i + 2]) && $needle[$ii + 1] !== $haystack[$i + 2]) {
  876. $count--;
  877. } else {
  878. $count++;
  879. }
  880. }
  881. }
  882. }
  883. }
  884. return $count;
  885. }
  886. /**
  887. * Get part of string
  888. *
  889. * @param string $string The string being checked.
  890. * @param integer $start The first position used in $string.
  891. * @param integer $length The maximum length of the returned string.
  892. * @return string The portion of $string specified by the $string and $length parameters.
  893. * @access public
  894. * @static
  895. */
  896. function substr($string, $start, $length = null) {
  897. if ($start === 0 && $length === null) {
  898. return $string;
  899. }
  900. $string = Multibyte::utf8($string);
  901. $stringCount = count($string);
  902. for ($i = 1; $i <= $start; $i++) {
  903. unset($string[$i - 1]);
  904. }
  905. if ($length === null || count($string) < $length) {
  906. return Multibyte::ascii($string);
  907. }
  908. $string = array_values($string);
  909. $value = array();
  910. for ($i = 0; $i < $length; $i++) {
  911. $value[] = $string[$i];
  912. }
  913. return Multibyte::ascii($value);
  914. }
  915. /**
  916. * Prepare a string for mail transport, using the provided encoding
  917. *
  918. * @param string $string value to encode
  919. * @param string $charset charset to use for encoding. defaults to UTF-8
  920. * @param string $newline
  921. * @return string
  922. * @access public
  923. * @static
  924. * @TODO: add support for 'Q'('Quoted Printable') encoding
  925. */
  926. function mimeEncode($string, $charset = null, $newline = "\r\n") {
  927. if (!Multibyte::checkMultibyte($string) && strlen($string) < 75) {
  928. return $string;
  929. }
  930. if (empty($charset)) {
  931. $charset = Configure::read('App.encoding');
  932. }
  933. $charset = strtoupper($charset);
  934. $start = '=?' . $charset . '?B?';
  935. $end = '?=';
  936. $spacer = $end . $newline . ' ' . $start;
  937. $length = 75 - strlen($start) - strlen($end);
  938. $length = $length - ($length % 4);
  939. if ($charset == 'UTF-8') {
  940. $parts = array();
  941. $maxchars = floor(($length * 3) / 4);
  942. while (strlen($string) > $maxchars) {
  943. $i = $maxchars;
  944. $test = ord($string[$i]);
  945. while ($test >= 128 && $test <= 191) {
  946. $i--;
  947. $test = ord($string[$i]);
  948. }
  949. $parts[] = base64_encode(substr($string, 0, $i));
  950. $string = substr($string, $i);
  951. }
  952. $parts[] = base64_encode($string);
  953. $string = implode($spacer, $parts);
  954. } else {
  955. $string = chunk_split(base64_encode($string), $length, $spacer);
  956. $string = preg_replace('/' . preg_quote($spacer) . '$/', '', $string);
  957. }
  958. return $start . $string . $end;
  959. }
  960. /**
  961. * Return the Code points range for Unicode characters
  962. *
  963. * @param interger $decimal
  964. * @return string
  965. * @access private
  966. */
  967. function __codepoint($decimal) {
  968. if ($decimal > 128 && $decimal < 256) {
  969. $return = '0080_00ff'; // Latin-1 Supplement
  970. } elseif ($decimal < 384) {
  971. $return = '0100_017f'; // Latin Extended-A
  972. } elseif ($decimal < 592) {
  973. $return = '0180_024F'; // Latin Extended-B
  974. } elseif ($decimal < 688) {
  975. $return = '0250_02af'; // IPA Extensions
  976. } elseif ($decimal >= 880 && $decimal < 1024) {
  977. $return = '0370_03ff'; // Greek and Coptic
  978. } elseif ($decimal < 1280) {
  979. $return = '0400_04ff'; // Cyrillic
  980. } elseif ($decimal < 1328) {
  981. $return = '0500_052f'; // Cyrillic Supplement
  982. } elseif ($decimal < 1424) {
  983. $return = '0530_058f'; // Armenian
  984. } elseif ($decimal >= 7680 && $decimal < 7936) {
  985. $return = '1e00_1eff'; // Latin Extended Additional
  986. } elseif ($decimal < 8192) {
  987. $return = '1f00_1fff'; // Greek Extended
  988. } elseif ($decimal >= 8448 && $decimal < 8528) {
  989. $return = '2100_214f'; // Letterlike Symbols
  990. } elseif ($decimal < 8592) {
  991. $return = '2150_218f'; // Number Forms
  992. } elseif ($decimal >= 9312 && $decimal < 9472) {
  993. $return = '2460_24ff'; // Enclosed Alphanumerics
  994. } elseif ($decimal >= 11264 && $decimal < 11360) {
  995. $return = '2c00_2c5f'; // Glagolitic
  996. } elseif ($decimal < 11392) {
  997. $return = '2c60_2c7f'; // Latin Extended-C
  998. } elseif ($decimal < 11520) {
  999. $return = '2c80_2cff'; // Coptic
  1000. } elseif ($decimal >= 65280 && $decimal < 65520) {
  1001. $return = 'ff00_ffef'; // Halfwidth and Fullwidth Forms
  1002. } else {
  1003. $return = false;
  1004. }
  1005. $this->__codeRange[$decimal] = $return;
  1006. return $return;
  1007. }
  1008. /**
  1009. * Find the related code folding values for $char
  1010. *
  1011. * @param integer $char decimal value of character
  1012. * @param string $type
  1013. * @return array
  1014. * @access private
  1015. */
  1016. function __find($char, $type = 'lower') {
  1017. $value = false;
  1018. $found = array();
  1019. if (!isset($this->__codeRange[$char])) {
  1020. $range = $this->__codepoint($char);
  1021. if ($range === false) {
  1022. return null;
  1023. }
  1024. Configure::load('unicode' . DS . 'casefolding' . DS . $range);
  1025. $this->__caseFold[$range] = Configure::read($range);
  1026. Configure::delete($range);
  1027. }
  1028. if (!$this->__codeRange[$char]) {
  1029. return null;
  1030. }
  1031. $this->__table = $this->__codeRange[$char];
  1032. $count = count($this->__caseFold[$this->__table]);
  1033. for ($i = 0; $i < $count; $i++) {
  1034. if ($type === 'lower' && $this->__caseFold[$this->__table][$i][$type][0] === $char) {
  1035. $found[] = $this->__caseFold[$this->__table][$i];
  1036. } elseif ($type === 'upper' && $this->__caseFold[$this->__table][$i][$type] === $char) {
  1037. $found[] = $this->__caseFold[$this->__table][$i];
  1038. }
  1039. }
  1040. return $found;
  1041. }
  1042. /**
  1043. * Check the $string for multibyte characters
  1044. * @param string $string value to test
  1045. * @return boolean
  1046. * @access public
  1047. * @static
  1048. */
  1049. function checkMultibyte($string) {
  1050. $length = strlen($string);
  1051. for ($i = 0; $i < $length; $i++ ) {
  1052. $value = ord(($string[$i]));
  1053. if ($value > 128) {
  1054. return true;
  1055. }
  1056. }
  1057. return false;
  1058. }
  1059. }
  1060. ?>