Subversion-Projekte lars-tiefland.cakephp

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/* SVN FILE: $Id: multibyte.php 8004 2009-01-16 20:15:21Z 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: 8004 $
22
 * @modifiedby    $LastChangedBy: gwoo $
23
 * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
24
 * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
25
 */
26
if (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
 */
41
if (!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
 */
57
if (!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
 */
70
if (!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
 */
85
if (!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
 */
101
if (!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
 */
117
if (!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
 */
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. If $needle is not found, it returns false.
145
 */
146
if (!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
 */
162
if (!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
 */
174
if (!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
 */
186
if (!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
 */
199
if (!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
 */
213
if (!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
 */
232
if (!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
 */
244
class 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
		$value = array();
970
		for ($i = 0; $i < $length; $i++) {
971
			$value[] = $string[$i];
972
		}
973
		return Multibyte::ascii($value);
974
	}
975
/**
976
 * Prepare a string for mail transport, using the provided encoding
977
 *
978
 * @param string $string value to encode
979
 * @param string $charset charset to use for encoding. defaults to UTF-8
980
 * @param string $newline
981
 * @return string
982
 * @access public
983
 * @static
984
 * @TODO: add support for 'Q'('Quoted Printable') encoding
985
 */
986
	function mimeEncode($string, $charset = null, $newline = "\r\n") {
987
		if (!Multibyte::checkMultibyte($string) && strlen($string) < 75) {
988
			return $string;
989
		}
990
 
991
		if (empty($charset)) {
992
			$charset = Configure::read('App.encoding');
993
		}
994
		$charset = strtoupper($charset);
995
 
996
		$start = '=?' . $charset . '?B?';
997
		$end = '?=';
998
		$spacer = $end . $newline . ' ' . $start;
999
 
1000
		$length = 75 - strlen($start) - strlen($end);
1001
		$length = $length - ($length % 4);
1002
		if ($charset == 'UTF-8') {
1003
			$parts = array();
1004
			$maxchars = floor(($length * 3) / 4);
1005
			while (strlen($string) > $maxchars) {
1006
				$i = $maxchars;
1007
				$test = ord($string[$i]);
1008
				while ($test >= 128 && $test <= 191) {
1009
					$i--;
1010
					$test = ord($string[$i]);
1011
				}
1012
				$parts[] = base64_encode(substr($string, 0, $i));
1013
				$string = substr($string, $i);
1014
			}
1015
			$parts[] = base64_encode($string);
1016
			$string = implode($spacer, $parts);
1017
		} else {
1018
			$string = chunk_split(base64_encode($string), $length, $spacer);
1019
			$string = preg_replace('/' . preg_quote($spacer) . '$/', '', $string);
1020
		}
1021
		return $start . $string . $end;
1022
	}
1023
/**
1024
 * Return the Code points range for Unicode characters
1025
 *
1026
 * @param interger $decimal
1027
 * @return string
1028
 * @access private
1029
 */
1030
	function __codepoint ($decimal) {
1031
		if ($decimal > 128 && $decimal < 256)  {
1032
			$return = '0080_00ff'; // Latin-1 Supplement
1033
		} elseif ($decimal < 384) {
1034
			$return = '0100_017f'; // Latin Extended-A
1035
		} elseif ($decimal < 592) {
1036
			$return = '0180_024F'; // Latin Extended-B
1037
		} elseif ($decimal < 688) {
1038
			$return = '0250_02af'; // IPA Extensions
1039
		} elseif ($decimal >= 880 && $decimal < 1024) {
1040
			$return = '0370_03ff'; // Greek and Coptic
1041
		} elseif ($decimal < 1280) {
1042
			$return = '0400_04ff'; // Cyrillic
1043
		} elseif ($decimal < 1328) {
1044
			$return = '0500_052f'; // Cyrillic Supplement
1045
		} elseif ($decimal < 1424) {
1046
			$return = '0530_058f'; // Armenian
1047
		} elseif ($decimal >= 7680 && $decimal < 7936) {
1048
			$return = '1e00_1eff'; // Latin Extended Additional
1049
		} elseif ($decimal < 8192) {
1050
			$return = '1f00_1fff'; // Greek Extended
1051
		} elseif ($decimal >= 8448 && $decimal < 8528) {
1052
			$return = '2100_214f'; // Letterlike Symbols
1053
		} elseif ($decimal < 8592) {
1054
			$return = '2150_218f'; // Number Forms
1055
		} elseif ($decimal >= 9312 && $decimal < 9472) {
1056
			$return = '2460_24ff'; // Enclosed Alphanumerics
1057
		} elseif ($decimal >= 11264 && $decimal < 11360) {
1058
			$return = '2c00_2c5f'; // Glagolitic
1059
		} elseif ($decimal < 11392) {
1060
			$return = '2c60_2c7f'; // Latin Extended-C
1061
		} elseif ($decimal < 11520) {
1062
			$return = '2c80_2cff'; // Coptic
1063
		} elseif ($decimal >= 65280 && $decimal < 65520) {
1064
			$return = 'ff00_ffef'; // Halfwidth and Fullwidth Forms
1065
		} else {
1066
			$return = false;
1067
		}
1068
		$this->__codeRange[$decimal] = $return;
1069
		return $return;
1070
	}
1071
/**
1072
 * Find the related code folding values for $char
1073
 *
1074
 * @param integer $char decimal value of character
1075
 * @param string $type
1076
 * @return array
1077
 * @access private
1078
 */
1079
	function __find($char, $type = 'lower') {
1080
		$value = false;
1081
		$found = array();
1082
		if (!isset($this->__codeRange[$char])) {
1083
			$range = $this->__codepoint($char);
1084
			if ($range === false) {
1085
				return null;
1086
			}
1087
			Configure::load('unicode' . DS . 'casefolding' . DS . $range);
1088
			$this->__caseFold[$range] = Configure::read($range);
1089
			Configure::delete($range);
1090
		}
1091
 
1092
		if (!$this->__codeRange[$char]) {
1093
			return null;
1094
		}
1095
		$this->__table = $this->__codeRange[$char];
1096
		$count = count($this->__caseFold[$this->__table]);
1097
 
1098
		for ($i = 0; $i < $count; $i++) {
1099
			if ($type === 'lower' && $this->__caseFold[$this->__table][$i][$type][0] === $char) {
1100
				$found[] = $this->__caseFold[$this->__table][$i];
1101
			} elseif ($type === 'upper' && $this->__caseFold[$this->__table][$i][$type] === $char) {
1102
				$found[] = $this->__caseFold[$this->__table][$i];
1103
			}
1104
		}
1105
		return $found;
1106
	}
1107
/**
1108
 * Check the $string for multibyte characters
1109
 * @param string $string value to test
1110
 * @return boolean
1111
 * @access public
1112
 * @static
1113
 */
1114
	function checkMultibyte($string) {
1115
		$length = strlen($string);
1116
 
1117
		for ($i = 0; $i < $length; $i++ ) {
1118
			$value = ord(($string[$i]));
1119
			if ($value > 128) {
1120
				return true;
1121
			}
1122
		}
1123
		return false;
1124
	}
1125
}
1126
?>