PageRenderTime 58ms CodeModel.GetById 38ms app.highlight 15ms RepoModel.GetById 1ms app.codeStats 0ms

/yii/framework/web/helpers/CJSON.php

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