root/trunk/server/www/cake/libs/multibyte.php @ 310

Revision 310, 36.6 KB (checked in by sander, 6 months ago)

Do not assign the return of 'new' by reference or PHP 5.3 will yell

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