Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

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