PageRenderTime 607ms CodeModel.GetById 126ms app.highlight 376ms RepoModel.GetById 98ms app.codeStats 0ms

/demo/yii/web/helpers/CJSON.php

https://bitbucket.org/univandro/yii-bootstrap
PHP | 708 lines | 429 code | 87 blank | 192 comment | 92 complexity | 764e8c640642f16f358a0581ce73ab32 MD5 | raw file
  1<?php
  2/**
  3* JSON (JavaScript Object Notation) is a lightweight data-interchange
  4* format. It is easy for humans to read and write. It is easy for machines
  5* to parse and generate. It is based on a subset of the JavaScript
  6* Programming Language, Standard ECMA-262 3rd Edition - December 1999.
  7* This feature can also be found in  Python. JSON is a text format that is
  8* completely language independent but uses conventions that are familiar
  9* to programmers of the C-family of languages, including C, C++, C#, Java,
 10* JavaScript, Perl, TCL, and many others. These properties make JSON an
 11* ideal data-interchange language.
 12*
 13* This package provides a simple encoder and decoder for JSON notation. It
 14* is intended for use with client-side Javascript applications that make
 15* use of HTTPRequest to perform server communication functions - data can
 16* be encoded into JSON notation for use in a client-side javascript, or
 17* decoded from incoming Javascript requests. JSON format is native to
 18* Javascript, and can be directly eval()'ed with no further parsing
 19* overhead
 20*
 21* All strings should be in ASCII or UTF-8 format!
 22*
 23* LICENSE: Redistribution and use in source and binary forms, with or
 24* without modification, are permitted provided that the following
 25* conditions are met: Redistributions of source code must retain the
 26* above copyright notice, this list of conditions and the following
 27* disclaimer. Redistributions in binary form must reproduce the above
 28* copyright notice, this list of conditions and the following disclaimer
 29* in the documentation and/or other materials provided with the
 30* distribution.
 31*
 32* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 33* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 34* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
 35* NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 36* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 37* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 38* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 39* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 40* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 41* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 42* DAMAGE.
 43*
 44* @author	  Michal Migurski <mike-json@teczno.com>
 45* @author	  Matt Knapp <mdknapp[at]gmail[dot]com>
 46* @author	  Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
 47* @copyright   2005 Michal Migurski
 48* @license	 http://www.opensource.org/licenses/bsd-license.php
 49* @link		http://pear.php.net/pepr/pepr-proposal-show.php?id=198
 50*/
 51
 52/**
 53 * CJSON converts PHP data to and from JSON format.
 54 *
 55 * @author	 Michal Migurski <mike-json@teczno.com>
 56 * @author	 Matt Knapp <mdknapp[at]gmail[dot]com>
 57 * @author	 Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
 58 * @version $Id$
 59 * @package	system.web.helpers
 60 * @since 1.0
 61 */
 62class CJSON
 63{
 64	/**
 65	 * Marker constant for JSON::decode(), used to flag stack state
 66	 */
 67	const JSON_SLICE = 1;
 68
 69	/**
 70	* Marker constant for JSON::decode(), used to flag stack state
 71	*/
 72	const JSON_IN_STR = 2;
 73
 74	/**
 75	* Marker constant for JSON::decode(), used to flag stack state
 76	*/
 77	const JSON_IN_ARR = 4;
 78
 79	/**
 80	* Marker constant for JSON::decode(), used to flag stack state
 81	*/
 82	const JSON_IN_OBJ = 8;
 83
 84	/**
 85	* Marker constant for JSON::decode(), used to flag stack state
 86	*/
 87	const JSON_IN_CMT = 16;
 88
 89   /**
 90	* Encodes an arbitrary variable into JSON format
 91	*
 92	* @param mixed $var any number, boolean, string, array, or object to be encoded.
 93	* If var is a string, it will be converted to UTF-8 format first before being encoded.
 94	* @return string JSON string representation of input var
 95	*/
 96	public static function encode($var)
 97	{
 98		switch (gettype($var)) {
 99			case 'boolean':
100				return $var ? 'true' : 'false';
101
102			case 'NULL':
103				return 'null';
104
105			case 'integer':
106				return (int) $var;
107
108			case 'double':
109			case 'float':
110				return str_replace(',','.',(float)$var); // locale-independent representation
111
112			case 'string':
113				if (($enc=strtoupper(Yii::app()->charset))!=='UTF-8')
114					$var=iconv($enc, 'UTF-8', $var);
115
116				if(function_exists('json_encode'))
117					return json_encode($var);
118
119				// STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
120				$ascii = '';
121				$strlen_var = strlen($var);
122
123			   /*
124				* Iterate over every character in the string,
125				* escaping with a slash or encoding to UTF-8 where necessary
126				*/
127				for ($c = 0; $c < $strlen_var; ++$c) {
128
129					$ord_var_c = ord($var{$c});
130
131					switch (true) {
132						case $ord_var_c == 0x08:
133							$ascii .= '\b';
134							break;
135						case $ord_var_c == 0x09:
136							$ascii .= '\t';
137							break;
138						case $ord_var_c == 0x0A:
139							$ascii .= '\n';
140							break;
141						case $ord_var_c == 0x0C:
142							$ascii .= '\f';
143							break;
144						case $ord_var_c == 0x0D:
145							$ascii .= '\r';
146							break;
147
148						case $ord_var_c == 0x22:
149						case $ord_var_c == 0x2F:
150						case $ord_var_c == 0x5C:
151							// double quote, slash, slosh
152							$ascii .= '\\'.$var{$c};
153							break;
154
155						case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
156							// characters U-00000000 - U-0000007F (same as ASCII)
157							$ascii .= $var{$c};
158							break;
159
160						case (($ord_var_c & 0xE0) == 0xC0):
161							// characters U-00000080 - U-000007FF, mask 110XXXXX
162							// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
163							$char = pack('C*', $ord_var_c, ord($var{$c+1}));
164							$c+=1;
165							$utf16 =  self::utf8ToUTF16BE($char);
166							$ascii .= sprintf('\u%04s', bin2hex($utf16));
167							break;
168
169						case (($ord_var_c & 0xF0) == 0xE0):
170							// characters U-00000800 - U-0000FFFF, mask 1110XXXX
171							// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
172							$char = pack('C*', $ord_var_c,
173										 ord($var{$c+1}),
174										 ord($var{$c+2}));
175							$c+=2;
176							$utf16 = self::utf8ToUTF16BE($char);
177							$ascii .= sprintf('\u%04s', bin2hex($utf16));
178							break;
179
180						case (($ord_var_c & 0xF8) == 0xF0):
181							// characters U-00010000 - U-001FFFFF, mask 11110XXX
182							// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
183							$char = pack('C*', $ord_var_c,
184										 ord($var{$c+1}),
185										 ord($var{$c+2}),
186										 ord($var{$c+3}));
187							$c+=3;
188							$utf16 = self::utf8ToUTF16BE($char);
189							$ascii .= sprintf('\u%04s', bin2hex($utf16));
190							break;
191
192						case (($ord_var_c & 0xFC) == 0xF8):
193							// characters U-00200000 - U-03FFFFFF, mask 111110XX
194							// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
195							$char = pack('C*', $ord_var_c,
196										 ord($var{$c+1}),
197										 ord($var{$c+2}),
198										 ord($var{$c+3}),
199										 ord($var{$c+4}));
200							$c+=4;
201							$utf16 = self::utf8ToUTF16BE($char);
202							$ascii .= sprintf('\u%04s', bin2hex($utf16));
203							break;
204
205						case (($ord_var_c & 0xFE) == 0xFC):
206							// characters U-04000000 - U-7FFFFFFF, mask 1111110X
207							// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
208							$char = pack('C*', $ord_var_c,
209										 ord($var{$c+1}),
210										 ord($var{$c+2}),
211										 ord($var{$c+3}),
212										 ord($var{$c+4}),
213										 ord($var{$c+5}));
214							$c+=5;
215							$utf16 = self::utf8ToUTF16BE($char);
216							$ascii .= sprintf('\u%04s', bin2hex($utf16));
217							break;
218					}
219				}
220
221				return '"'.$ascii.'"';
222
223			case 'array':
224			   /*
225				* As per JSON spec if any array key is not an integer
226				* we must treat the the whole array as an object. We
227				* also try to catch a sparsely populated associative
228				* array with numeric keys here because some JS engines
229				* will create an array with empty indexes up to
230				* max_index which can cause memory issues and because
231				* the keys, which may be relevant, will be remapped
232				* otherwise.
233				*
234				* As per the ECMA and JSON specification an object may
235				* have any string as a property. Unfortunately due to
236				* a hole in the ECMA specification if the key is a
237				* ECMA reserved word or starts with a digit the
238				* parameter is only accessible using ECMAScript's
239				* bracket notation.
240				*/
241
242				// treat as a JSON object
243				if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
244					return '{' .
245						   join(',', array_map(array('CJSON', 'nameValue'),
246											   array_keys($var),
247											   array_values($var)))
248						   . '}';
249				}
250
251				// treat it like a regular array
252				return '[' . join(',', array_map(array('CJSON', 'encode'), $var)) . ']';
253
254			case 'object':
255				if ($var instanceof Traversable)
256				{
257					$vars = array();
258					foreach ($var as $k=>$v)
259						$vars[$k] = $v;
260				}
261				else
262					$vars = get_object_vars($var);
263				return '{' .
264					   join(',', array_map(array('CJSON', 'nameValue'),
265										   array_keys($vars),
266										   array_values($vars)))
267					   . '}';
268
269			default:
270				return '';
271		}
272	}
273
274   /**
275	* array-walking function for use in generating JSON-formatted name-value pairs
276	*
277	* @param string $name  name of key to use
278	* @param mixed $value reference to an array element to be encoded
279	*
280	* @return   string  JSON-formatted name-value pair, like '"name":value'
281	* @access   private
282	*/
283	protected static function nameValue($name, $value)
284	{
285		return self::encode(strval($name)) . ':' . self::encode($value);
286	}
287
288   /**
289	* reduce a string by removing leading and trailing comments and whitespace
290	*
291	* @param string $str string value to strip of comments and whitespace
292	*
293	* @return string string value stripped of comments and whitespace
294	* @access   private
295	*/
296	protected static function reduceString($str)
297	{
298		$str = preg_replace(array(
299
300				// eliminate single line comments in '// ...' form
301				'#^\s*//(.+)$#m',
302
303				// eliminate multi-line comments in '/* ... */' form, at start of string
304				'#^\s*/\*(.+)\*/#Us',
305
306				// eliminate multi-line comments in '/* ... */' form, at end of string
307				'#/\*(.+)\*/\s*$#Us'
308
309			), '', $str);
310
311		// eliminate extraneous space
312		return trim($str);
313	}
314
315   /**
316	* decodes a JSON string into appropriate variable
317	*
318	* @param string $str  JSON-formatted string
319	* @param boolean $useArray  whether to use associative array to represent object data
320	* @return mixed   number, boolean, string, array, or object corresponding to given JSON input string.
321	*    Note that decode() always returns strings in ASCII or UTF-8 format!
322	* @access   public
323	*/
324	public static function decode($str, $useArray=true)
325	{
326		if(function_exists('json_decode'))
327			$json = json_decode($str,$useArray);
328
329		// based on investigation, native fails sometimes returning null.
330		// see: http://gggeek.altervista.org/sw/article_20070425.html
331		// As of PHP 5.3.6 it still fails on some valid JSON strings
332		if(!is_null($json))
333			return $json;
334
335		$str = self::reduceString($str);
336
337		switch (strtolower($str)) {
338			case 'true':
339				return true;
340
341			case 'false':
342				return false;
343
344			case 'null':
345				return null;
346
347			default:
348				if (is_numeric($str)) {
349					// Lookie-loo, it's a number
350
351					// This would work on its own, but I'm trying to be
352					// good about returning integers where appropriate:
353					// return (float)$str;
354
355					// Return float or int, as appropriate
356					return ((float)$str == (integer)$str)
357						? (integer)$str
358						: (float)$str;
359
360				} elseif (preg_match('/^("|\').+(\1)$/s', $str, $m) && $m[1] == $m[2]) {
361					// STRINGS RETURNED IN UTF-8 FORMAT
362					$delim = substr($str, 0, 1);
363					$chrs = substr($str, 1, -1);
364					$utf8 = '';
365					$strlen_chrs = strlen($chrs);
366
367					for ($c = 0; $c < $strlen_chrs; ++$c) {
368
369						$substr_chrs_c_2 = substr($chrs, $c, 2);
370						$ord_chrs_c = ord($chrs{$c});
371
372						switch (true) {
373							case $substr_chrs_c_2 == '\b':
374								$utf8 .= chr(0x08);
375								++$c;
376								break;
377							case $substr_chrs_c_2 == '\t':
378								$utf8 .= chr(0x09);
379								++$c;
380								break;
381							case $substr_chrs_c_2 == '\n':
382								$utf8 .= chr(0x0A);
383								++$c;
384								break;
385							case $substr_chrs_c_2 == '\f':
386								$utf8 .= chr(0x0C);
387								++$c;
388								break;
389							case $substr_chrs_c_2 == '\r':
390								$utf8 .= chr(0x0D);
391								++$c;
392								break;
393
394							case $substr_chrs_c_2 == '\\"':
395							case $substr_chrs_c_2 == '\\\'':
396							case $substr_chrs_c_2 == '\\\\':
397							case $substr_chrs_c_2 == '\\/':
398								if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
399								   ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
400									$utf8 .= $chrs{++$c};
401								}
402								break;
403
404							case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
405								// single, escaped unicode character
406								$utf16 = chr(hexdec(substr($chrs, ($c+2), 2)))
407									   . chr(hexdec(substr($chrs, ($c+4), 2)));
408								$utf8 .= self::utf16beToUTF8($utf16);
409								$c+=5;
410								break;
411
412							case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
413								$utf8 .= $chrs{$c};
414								break;
415
416							case ($ord_chrs_c & 0xE0) == 0xC0:
417								// characters U-00000080 - U-000007FF, mask 110XXXXX
418								//see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
419								$utf8 .= substr($chrs, $c, 2);
420								++$c;
421								break;
422
423							case ($ord_chrs_c & 0xF0) == 0xE0:
424								// characters U-00000800 - U-0000FFFF, mask 1110XXXX
425								// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
426								$utf8 .= substr($chrs, $c, 3);
427								$c += 2;
428								break;
429
430							case ($ord_chrs_c & 0xF8) == 0xF0:
431								// characters U-00010000 - U-001FFFFF, mask 11110XXX
432								// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
433								$utf8 .= substr($chrs, $c, 4);
434								$c += 3;
435								break;
436
437							case ($ord_chrs_c & 0xFC) == 0xF8:
438								// characters U-00200000 - U-03FFFFFF, mask 111110XX
439								// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
440								$utf8 .= substr($chrs, $c, 5);
441								$c += 4;
442								break;
443
444							case ($ord_chrs_c & 0xFE) == 0xFC:
445								// characters U-04000000 - U-7FFFFFFF, mask 1111110X
446								// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
447								$utf8 .= substr($chrs, $c, 6);
448								$c += 5;
449								break;
450
451						}
452
453					}
454
455					return $utf8;
456
457				} elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
458					// array, or object notation
459
460					if ($str{0} == '[') {
461						$stk = array(self::JSON_IN_ARR);
462						$arr = array();
463					} else {
464						if ($useArray) {
465							$stk = array(self::JSON_IN_OBJ);
466							$obj = array();
467						} else {
468							$stk = array(self::JSON_IN_OBJ);
469							$obj = new stdClass();
470						}
471					}
472
473					$stk[] = array('what' => self::JSON_SLICE, 'where' => 0, 'delim' => false);
474
475					$chrs = substr($str, 1, -1);
476					$chrs = self::reduceString($chrs);
477
478					if ($chrs == '') {
479						if (reset($stk) == self::JSON_IN_ARR) {
480							return $arr;
481
482						} else {
483							return $obj;
484
485						}
486					}
487
488					//print("\nparsing {$chrs}\n");
489
490					$strlen_chrs = strlen($chrs);
491
492					for ($c = 0; $c <= $strlen_chrs; ++$c) {
493
494						$top = end($stk);
495						$substr_chrs_c_2 = substr($chrs, $c, 2);
496
497						if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == self::JSON_SLICE))) {
498							// found a comma that is not inside a string, array, etc.,
499							// OR we've reached the end of the character list
500							$slice = substr($chrs, $top['where'], ($c - $top['where']));
501							$stk[] = array('what' => self::JSON_SLICE, 'where' => ($c + 1), 'delim' => false);
502							//print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
503
504							if (reset($stk) == self::JSON_IN_ARR) {
505								// we are in an array, so just push an element onto the stack
506								$arr[] = self::decode($slice,$useArray);
507
508							} elseif (reset($stk) == self::JSON_IN_OBJ) {
509								// we are in an object, so figure
510								// out the property name and set an
511								// element in an associative array,
512								// for now
513								if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
514									// "name":value pair
515									$key = self::decode($parts[1],$useArray);
516									$val = self::decode($parts[2],$useArray);
517
518									if ($useArray) {
519										$obj[$key] = $val;
520									} else {
521										$obj->$key = $val;
522									}
523								} elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
524									// name:value pair, where name is unquoted
525									$key = $parts[1];
526									$val = self::decode($parts[2],$useArray);
527
528									if ($useArray) {
529										$obj[$key] = $val;
530									} else {
531										$obj->$key = $val;
532									}
533								}
534
535							}
536
537						} elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != self::JSON_IN_STR)) {
538							// found a quote, and we are not inside a string
539							$stk[] = array('what' => self::JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c});
540							//print("Found start of string at {$c}\n");
541
542						} elseif (($chrs{$c} == $top['delim']) &&
543								 ($top['what'] == self::JSON_IN_STR) &&
544								 (($chrs{$c - 1} != "\\") ||
545								 ($chrs{$c - 1} == "\\" && $chrs{$c - 2} == "\\"))) {
546							// found a quote, we're in a string, and it's not escaped
547							array_pop($stk);
548							//print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
549
550						} elseif (($chrs{$c} == '[') &&
551								 in_array($top['what'], array(self::JSON_SLICE, self::JSON_IN_ARR, self::JSON_IN_OBJ))) {
552							// found a left-bracket, and we are in an array, object, or slice
553							$stk[] = array('what' => self::JSON_IN_ARR, 'where' => $c, 'delim' => false);
554							//print("Found start of array at {$c}\n");
555
556						} elseif (($chrs{$c} == ']') && ($top['what'] == self::JSON_IN_ARR)) {
557							// found a right-bracket, and we're in an array
558							array_pop($stk);
559							//print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
560
561						} elseif (($chrs{$c} == '{') &&
562								 in_array($top['what'], array(self::JSON_SLICE, self::JSON_IN_ARR, self::JSON_IN_OBJ))) {
563							// found a left-brace, and we are in an array, object, or slice
564							$stk[] = array('what' => self::JSON_IN_OBJ, 'where' => $c, 'delim' => false);
565							//print("Found start of object at {$c}\n");
566
567						} elseif (($chrs{$c} == '}') && ($top['what'] == self::JSON_IN_OBJ)) {
568							// found a right-brace, and we're in an object
569							array_pop($stk);
570							//print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
571
572						} elseif (($substr_chrs_c_2 == '/*') &&
573								 in_array($top['what'], array(self::JSON_SLICE, self::JSON_IN_ARR, self::JSON_IN_OBJ))) {
574							// found a comment start, and we are in an array, object, or slice
575							$stk[] = array('what' => self::JSON_IN_CMT, 'where' => $c, 'delim' => false);
576							$c++;
577							//print("Found start of comment at {$c}\n");
578
579						} elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == self::JSON_IN_CMT)) {
580							// found a comment end, and we're in one now
581							array_pop($stk);
582							$c++;
583
584							for ($i = $top['where']; $i <= $c; ++$i)
585								$chrs = substr_replace($chrs, ' ', $i, 1);
586
587							//print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
588
589						}
590
591					}
592
593					if (reset($stk) == self::JSON_IN_ARR) {
594						return $arr;
595
596					} elseif (reset($stk) == self::JSON_IN_OBJ) {
597						return $obj;
598
599					}
600
601				}
602		}
603	}
604
605	/**
606	* This function returns any UTF-8 encoded text as a list of
607	* Unicode values:
608	* @param string $str string to convert
609	* @return string
610	* @author Scott Michael Reynen <scott@randomchaos.com>
611	* @link   http://www.randomchaos.com/document.php?source=php_and_unicode
612	* @see	unicodeToUTF8()
613	*/
614	protected static function utf8ToUnicode( &$str )
615	{
616		$unicode = array();
617		$values = array();
618		$lookingFor = 1;
619
620		for ($i = 0; $i < strlen( $str ); $i++ )
621		{
622			$thisValue = ord( $str[ $i ] );
623			if ( $thisValue < 128 )
624				$unicode[] = $thisValue;
625			else
626			{
627				if ( count( $values ) == 0 )
628					$lookingFor = ( $thisValue < 224 ) ? 2 : 3;
629				$values[] = $thisValue;
630				if ( count( $values ) == $lookingFor )
631				{
632					$number = ( $lookingFor == 3 ) ?
633						( ( $values[0] % 16 ) * 4096 ) + ( ( $values[1] % 64 ) * 64 ) + ( $values[2] % 64 ):
634						( ( $values[0] % 32 ) * 64 ) + ( $values[1] % 64 );
635					$unicode[] = $number;
636					$values = array();
637					$lookingFor = 1;
638				}
639			}
640		}
641		return $unicode;
642	}
643
644	/**
645	* This function converts a Unicode array back to its UTF-8 representation
646	* @param string $str string to convert
647	* @return string
648	* @author Scott Michael Reynen <scott@randomchaos.com>
649	* @link   http://www.randomchaos.com/document.php?source=php_and_unicode
650	* @see	utf8ToUnicode()
651	*/
652	protected static function unicodeToUTF8( &$str )
653	{
654		$utf8 = '';
655		foreach( $str as $unicode )
656		{
657			if ( $unicode < 128 )
658			{
659				$utf8.= chr( $unicode );
660			}
661			elseif ( $unicode < 2048 )
662			{
663				$utf8.= chr( 192 +  ( ( $unicode - ( $unicode % 64 ) ) / 64 ) );
664				$utf8.= chr( 128 + ( $unicode % 64 ) );
665			}
666			else
667			{
668				$utf8.= chr( 224 + ( ( $unicode - ( $unicode % 4096 ) ) / 4096 ) );
669				$utf8.= chr( 128 + ( ( ( $unicode % 4096 ) - ( $unicode % 64 ) ) / 64 ) );
670				$utf8.= chr( 128 + ( $unicode % 64 ) );
671			}
672		}
673		return $utf8;
674	}
675
676	/**
677	* UTF-8 to UTF-16BE conversion.
678	*
679	* Maybe really UCS-2 without mb_string due to utf8ToUnicode limits
680	* @param string $str string to convert
681	* @param boolean $bom whether to output BOM header
682	* @return string
683	*/
684	protected static function utf8ToUTF16BE(&$str, $bom = false)
685	{
686		$out = $bom ? "\xFE\xFF" : '';
687		if(function_exists('mb_convert_encoding'))
688			return $out.mb_convert_encoding($str,'UTF-16BE','UTF-8');
689
690		$uni = self::utf8ToUnicode($str);
691		foreach($uni as $cp)
692			$out .= pack('n',$cp);
693		return $out;
694	}
695
696	/**
697	 * UTF-8 to UTF-16BE conversion.
698	 *
699	 * Maybe really UCS-2 without mb_string due to utf8ToUnicode limits
700	 * @param string $str string to convert
701	 * @return string
702	 */
703	protected static function utf16beToUTF8(&$str)
704	{
705		$uni = unpack('n*',$str);
706		return self::unicodeToUTF8($uni);
707	}
708}