PageRenderTime 99ms CodeModel.GetById 47ms app.highlight 44ms RepoModel.GetById 1ms app.codeStats 0ms

/framework/Web/Javascripts/TJSON.php

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