source: Dev/branches/rest-dojo-ui/client/dojo/tests/resources/JSON.php @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

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