PluginProbe ʕ •ᴥ•ʔ
Broken Link Checker / 0.5
Broken Link Checker v0.5
1.5.4 1.5.5 1.6 1.6.1 1.6.2 1.7 1.7.1 1.8 1.8.1 1.8.2 1.8.3 1.9 1.9.1 1.9.2 1.9.3 1.9.4 1.9.4.1 1.9.4.2 1.9.5 2.0.0 2.1.0 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.3.1 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.4.8 0.9.4 0.9.4.1 0.9.4.2 0.9.4.3 0.9.4.4 0.9.4.4-last-non-modular 0.9.5 0.9.6 0.9.7 0.9.7.1 0.9.7.2 1.10 1.10.1 1.10.10 1.10.11 1.10.2 1.10.3 1.10.4 1.10.5 1.10.6 1.10.7 1.10.8 1.10.9 1.11.1 1.11.10 1.11.11 1.11.12 1.11.13 1.11.14 1.11.15 1.11.17 1.11.18 1.11.19 1.11.2 1.11.20 1.11.21 1.11.3 1.11.4 1.11.5 1.11.8 1.11.9 1.2.2 1.2.3 1.2.4 1.2.5 1.3 1.3.1 1.4 1.5 1.5.1 1.5.2 1.5.3 trunk 0.1 0.2 0.2.2 0.2.2.1 0.2.3 0.2.4 0.2.5 0.3 0.3.1 0.3.2 0.3.3 0.3.4 0.3.5 0.3.6 0.3.7 0.3.8 0.3.9 0.4 0.4-i8n 0.4.1 0.4.10 0.4.11 0.4.12 0.4.13 0.4.14 0.4.2 0.4.3 0.4.4 0.4.5 0.4.6 0.4.7 0.4.8 0.4.9 0.5 0.5.1 0.5.10 0.5.10.1 0.5.11 0.5.12 0.5.13 0.5.14 0.5.15 0.5.16 0.5.16.1 0.5.17 0.5.18 0.5.2 0.5.3 0.5.4 0.5.5 0.5.6 0.5.7 0.5.8 0.5.8.1 0.5.9 0.6 0.6.1 0.6.2 0.6.3 0.6.4 0.6.5 0.7 0.7.1 0.7.2 0.7.3 0.7.4 0.8 0.8.1 0.9 0.9.1 0.9.2 0.9.3
broken-link-checker / JSON.php
broken-link-checker Last commit date
images 17 years ago JSON.php 17 years ago broken-link-checker.php 17 years ago instance-classes.php 17 years ago link-classes.php 17 years ago readme.txt 17 years ago uninstall.php 17 years ago utility-class.php 17 years ago wsblc_ajax.php 17 years ago
JSON.php
806 lines
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 * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
55 * @license http://www.opensource.org/licenses/bsd-license.php
56 * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
57 */
58
59 /**
60 * Marker constant for Services_JSON::decode(), used to flag stack state
61 */
62 define('SERVICES_JSON_SLICE', 1);
63
64 /**
65 * Marker constant for Services_JSON::decode(), used to flag stack state
66 */
67 define('SERVICES_JSON_IN_STR', 2);
68
69 /**
70 * Marker constant for Services_JSON::decode(), used to flag stack state
71 */
72 define('SERVICES_JSON_IN_ARR', 3);
73
74 /**
75 * Marker constant for Services_JSON::decode(), used to flag stack state
76 */
77 define('SERVICES_JSON_IN_OBJ', 4);
78
79 /**
80 * Marker constant for Services_JSON::decode(), used to flag stack state
81 */
82 define('SERVICES_JSON_IN_CMT', 5);
83
84 /**
85 * Behavior switch for Services_JSON::decode()
86 */
87 define('SERVICES_JSON_LOOSE_TYPE', 16);
88
89 /**
90 * Behavior switch for Services_JSON::decode()
91 */
92 define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
93
94 /**
95 * Converts to and from JSON format.
96 *
97 * Brief example of use:
98 *
99 * <code>
100 * // create a new instance of Services_JSON
101 * $json = new Services_JSON();
102 *
103 * // convert a complexe value to JSON notation, and send it to the browser
104 * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
105 * $output = $json->encode($value);
106 *
107 * print($output);
108 * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
109 *
110 * // accept incoming POST data, assumed to be in JSON notation
111 * $input = file_get_contents('php://input', 1000000);
112 * $value = $json->decode($input);
113 * </code>
114 */
115 class Services_JSON
116 {
117 /**
118 * constructs a new JSON instance
119 *
120 * @param int $use object behavior flags; combine with boolean-OR
121 *
122 * possible values:
123 * - SERVICES_JSON_LOOSE_TYPE: loose typing.
124 * "{...}" syntax creates associative arrays
125 * instead of objects in decode().
126 * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
127 * Values which can't be encoded (e.g. resources)
128 * appear as NULL instead of throwing errors.
129 * By default, a deeply-nested resource will
130 * bubble up with an error, so all return values
131 * from encode() should be checked with isError()
132 */
133 function Services_JSON($use = 0)
134 {
135 $this->use = $use;
136 }
137
138 /**
139 * convert a string from one UTF-16 char to one UTF-8 char
140 *
141 * Normally should be handled by mb_convert_encoding, but
142 * provides a slower PHP-only method for installations
143 * that lack the multibye string extension.
144 *
145 * @param string $utf16 UTF-16 character
146 * @return string UTF-8 character
147 * @access private
148 */
149 function utf162utf8($utf16)
150 {
151 // oh please oh please oh please oh please oh please
152 if(function_exists('mb_convert_encoding')) {
153 return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
154 }
155
156 $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
157
158 switch(true) {
159 case ((0x7F & $bytes) == $bytes):
160 // this case should never be reached, because we are in ASCII range
161 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
162 return chr(0x7F & $bytes);
163
164 case (0x07FF & $bytes) == $bytes:
165 // return a 2-byte UTF-8 character
166 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
167 return chr(0xC0 | (($bytes >> 6) & 0x1F))
168 . chr(0x80 | ($bytes & 0x3F));
169
170 case (0xFFFF & $bytes) == $bytes:
171 // return a 3-byte UTF-8 character
172 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
173 return chr(0xE0 | (($bytes >> 12) & 0x0F))
174 . chr(0x80 | (($bytes >> 6) & 0x3F))
175 . chr(0x80 | ($bytes & 0x3F));
176 }
177
178 // ignoring UTF-32 for now, sorry
179 return '';
180 }
181
182 /**
183 * convert a string from one UTF-8 char to one UTF-16 char
184 *
185 * Normally should be handled by mb_convert_encoding, but
186 * provides a slower PHP-only method for installations
187 * that lack the multibye string extension.
188 *
189 * @param string $utf8 UTF-8 character
190 * @return string UTF-16 character
191 * @access private
192 */
193 function utf82utf16($utf8)
194 {
195 // oh please oh please oh please oh please oh please
196 if(function_exists('mb_convert_encoding')) {
197 return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
198 }
199
200 switch(strlen($utf8)) {
201 case 1:
202 // this case should never be reached, because we are in ASCII range
203 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
204 return $utf8;
205
206 case 2:
207 // return a UTF-16 character from a 2-byte UTF-8 char
208 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
209 return chr(0x07 & (ord($utf8{0}) >> 2))
210 . chr((0xC0 & (ord($utf8{0}) << 6))
211 | (0x3F & ord($utf8{1})));
212
213 case 3:
214 // return a UTF-16 character from a 3-byte UTF-8 char
215 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
216 return chr((0xF0 & (ord($utf8{0}) << 4))
217 | (0x0F & (ord($utf8{1}) >> 2)))
218 . chr((0xC0 & (ord($utf8{1}) << 6))
219 | (0x7F & ord($utf8{2})));
220 }
221
222 // ignoring UTF-32 for now, sorry
223 return '';
224 }
225
226 /**
227 * encodes an arbitrary variable into JSON format
228 *
229 * @param mixed $var any number, boolean, string, array, or object to be encoded.
230 * see argument 1 to Services_JSON() above for array-parsing behavior.
231 * if var is a strng, note that encode() always expects it
232 * to be in ASCII or UTF-8 format!
233 *
234 * @return mixed JSON string representation of input var or an error if a problem occurs
235 * @access public
236 */
237 function encode($var)
238 {
239 switch (gettype($var)) {
240 case 'boolean':
241 return $var ? 'true' : 'false';
242
243 case 'NULL':
244 return 'null';
245
246 case 'integer':
247 return (int) $var;
248
249 case 'double':
250 case 'float':
251 return (float) $var;
252
253 case 'string':
254 // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
255 $ascii = '';
256 $strlen_var = strlen($var);
257
258 /*
259 * Iterate over every character in the string,
260 * escaping with a slash or encoding to UTF-8 where necessary
261 */
262 for ($c = 0; $c < $strlen_var; ++$c) {
263
264 $ord_var_c = ord($var{$c});
265
266 switch (true) {
267 case $ord_var_c == 0x08:
268 $ascii .= '\b';
269 break;
270 case $ord_var_c == 0x09:
271 $ascii .= '\t';
272 break;
273 case $ord_var_c == 0x0A:
274 $ascii .= '\n';
275 break;
276 case $ord_var_c == 0x0C:
277 $ascii .= '\f';
278 break;
279 case $ord_var_c == 0x0D:
280 $ascii .= '\r';
281 break;
282
283 case $ord_var_c == 0x22:
284 case $ord_var_c == 0x2F:
285 case $ord_var_c == 0x5C:
286 // double quote, slash, slosh
287 $ascii .= '\\'.$var{$c};
288 break;
289
290 case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
291 // characters U-00000000 - U-0000007F (same as ASCII)
292 $ascii .= $var{$c};
293 break;
294
295 case (($ord_var_c & 0xE0) == 0xC0):
296 // characters U-00000080 - U-000007FF, mask 110XXXXX
297 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
298 $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
299 $c += 1;
300 $utf16 = $this->utf82utf16($char);
301 $ascii .= sprintf('\u%04s', bin2hex($utf16));
302 break;
303
304 case (($ord_var_c & 0xF0) == 0xE0):
305 // characters U-00000800 - U-0000FFFF, mask 1110XXXX
306 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
307 $char = pack('C*', $ord_var_c,
308 ord($var{$c + 1}),
309 ord($var{$c + 2}));
310 $c += 2;
311 $utf16 = $this->utf82utf16($char);
312 $ascii .= sprintf('\u%04s', bin2hex($utf16));
313 break;
314
315 case (($ord_var_c & 0xF8) == 0xF0):
316 // characters U-00010000 - U-001FFFFF, mask 11110XXX
317 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
318 $char = pack('C*', $ord_var_c,
319 ord($var{$c + 1}),
320 ord($var{$c + 2}),
321 ord($var{$c + 3}));
322 $c += 3;
323 $utf16 = $this->utf82utf16($char);
324 $ascii .= sprintf('\u%04s', bin2hex($utf16));
325 break;
326
327 case (($ord_var_c & 0xFC) == 0xF8):
328 // characters U-00200000 - U-03FFFFFF, mask 111110XX
329 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
330 $char = pack('C*', $ord_var_c,
331 ord($var{$c + 1}),
332 ord($var{$c + 2}),
333 ord($var{$c + 3}),
334 ord($var{$c + 4}));
335 $c += 4;
336 $utf16 = $this->utf82utf16($char);
337 $ascii .= sprintf('\u%04s', bin2hex($utf16));
338 break;
339
340 case (($ord_var_c & 0xFE) == 0xFC):
341 // characters U-04000000 - U-7FFFFFFF, mask 1111110X
342 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
343 $char = pack('C*', $ord_var_c,
344 ord($var{$c + 1}),
345 ord($var{$c + 2}),
346 ord($var{$c + 3}),
347 ord($var{$c + 4}),
348 ord($var{$c + 5}));
349 $c += 5;
350 $utf16 = $this->utf82utf16($char);
351 $ascii .= sprintf('\u%04s', bin2hex($utf16));
352 break;
353 }
354 }
355
356 return '"'.$ascii.'"';
357
358 case 'array':
359 /*
360 * As per JSON spec if any array key is not an integer
361 * we must treat the the whole array as an object. We
362 * also try to catch a sparsely populated associative
363 * array with numeric keys here because some JS engines
364 * will create an array with empty indexes up to
365 * max_index which can cause memory issues and because
366 * the keys, which may be relevant, will be remapped
367 * otherwise.
368 *
369 * As per the ECMA and JSON specification an object may
370 * have any string as a property. Unfortunately due to
371 * a hole in the ECMA specification if the key is a
372 * ECMA reserved word or starts with a digit the
373 * parameter is only accessible using ECMAScript's
374 * bracket notation.
375 */
376
377 // treat as a JSON object
378 if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
379 $properties = array_map(array($this, 'name_value'),
380 array_keys($var),
381 array_values($var));
382
383 foreach($properties as $property) {
384 if(Services_JSON::isError($property)) {
385 return $property;
386 }
387 }
388
389 return '{' . join(',', $properties) . '}';
390 }
391
392 // treat it like a regular array
393 $elements = array_map(array($this, 'encode'), $var);
394
395 foreach($elements as $element) {
396 if(Services_JSON::isError($element)) {
397 return $element;
398 }
399 }
400
401 return '[' . join(',', $elements) . ']';
402
403 case 'object':
404 $vars = get_object_vars($var);
405
406 $properties = array_map(array($this, 'name_value'),
407 array_keys($vars),
408 array_values($vars));
409
410 foreach($properties as $property) {
411 if(Services_JSON::isError($property)) {
412 return $property;
413 }
414 }
415
416 return '{' . join(',', $properties) . '}';
417
418 default:
419 return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
420 ? 'null'
421 : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
422 }
423 }
424
425 /**
426 * array-walking function for use in generating JSON-formatted name-value pairs
427 *
428 * @param string $name name of key to use
429 * @param mixed $value reference to an array element to be encoded
430 *
431 * @return string JSON-formatted name-value pair, like '"name":value'
432 * @access private
433 */
434 function name_value($name, $value)
435 {
436 $encoded_value = $this->encode($value);
437
438 if(Services_JSON::isError($encoded_value)) {
439 return $encoded_value;
440 }
441
442 return $this->encode(strval($name)) . ':' . $encoded_value;
443 }
444
445 /**
446 * reduce a string by removing leading and trailing comments and whitespace
447 *
448 * @param $str string string value to strip of comments and whitespace
449 *
450 * @return string string value stripped of comments and whitespace
451 * @access private
452 */
453 function reduce_string($str)
454 {
455 $str = preg_replace(array(
456
457 // eliminate single line comments in '// ...' form
458 '#^\s*//(.+)$#m',
459
460 // eliminate multi-line comments in '/* ... */' form, at start of string
461 '#^\s*/\*(.+)\*/#Us',
462
463 // eliminate multi-line comments in '/* ... */' form, at end of string
464 '#/\*(.+)\*/\s*$#Us'
465
466 ), '', $str);
467
468 // eliminate extraneous space
469 return trim($str);
470 }
471
472 /**
473 * decodes a JSON string into appropriate variable
474 *
475 * @param string $str JSON-formatted string
476 *
477 * @return mixed number, boolean, string, array, or object
478 * corresponding to given JSON input string.
479 * See argument 1 to Services_JSON() above for object-output behavior.
480 * Note that decode() always returns strings
481 * in ASCII or UTF-8 format!
482 * @access public
483 */
484 function decode($str)
485 {
486 $str = $this->reduce_string($str);
487
488 switch (strtolower($str)) {
489 case 'true':
490 return true;
491
492 case 'false':
493 return false;
494
495 case 'null':
496 return null;
497
498 default:
499 $m = array();
500
501 if (is_numeric($str)) {
502 // Lookie-loo, it's a number
503
504 // This would work on its own, but I'm trying to be
505 // good about returning integers where appropriate:
506 // return (float)$str;
507
508 // Return float or int, as appropriate
509 return ((float)$str == (integer)$str)
510 ? (integer)$str
511 : (float)$str;
512
513 } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
514 // STRINGS RETURNED IN UTF-8 FORMAT
515 $delim = substr($str, 0, 1);
516 $chrs = substr($str, 1, -1);
517 $utf8 = '';
518 $strlen_chrs = strlen($chrs);
519
520 for ($c = 0; $c < $strlen_chrs; ++$c) {
521
522 $substr_chrs_c_2 = substr($chrs, $c, 2);
523 $ord_chrs_c = ord($chrs{$c});
524
525 switch (true) {
526 case $substr_chrs_c_2 == '\b':
527 $utf8 .= chr(0x08);
528 ++$c;
529 break;
530 case $substr_chrs_c_2 == '\t':
531 $utf8 .= chr(0x09);
532 ++$c;
533 break;
534 case $substr_chrs_c_2 == '\n':
535 $utf8 .= chr(0x0A);
536 ++$c;
537 break;
538 case $substr_chrs_c_2 == '\f':
539 $utf8 .= chr(0x0C);
540 ++$c;
541 break;
542 case $substr_chrs_c_2 == '\r':
543 $utf8 .= chr(0x0D);
544 ++$c;
545 break;
546
547 case $substr_chrs_c_2 == '\\"':
548 case $substr_chrs_c_2 == '\\\'':
549 case $substr_chrs_c_2 == '\\\\':
550 case $substr_chrs_c_2 == '\\/':
551 if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
552 ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
553 $utf8 .= $chrs{++$c};
554 }
555 break;
556
557 case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
558 // single, escaped unicode character
559 $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
560 . chr(hexdec(substr($chrs, ($c + 4), 2)));
561 $utf8 .= $this->utf162utf8($utf16);
562 $c += 5;
563 break;
564
565 case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
566 $utf8 .= $chrs{$c};
567 break;
568
569 case ($ord_chrs_c & 0xE0) == 0xC0:
570 // characters U-00000080 - U-000007FF, mask 110XXXXX
571 //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
572 $utf8 .= substr($chrs, $c, 2);
573 ++$c;
574 break;
575
576 case ($ord_chrs_c & 0xF0) == 0xE0:
577 // characters U-00000800 - U-0000FFFF, mask 1110XXXX
578 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
579 $utf8 .= substr($chrs, $c, 3);
580 $c += 2;
581 break;
582
583 case ($ord_chrs_c & 0xF8) == 0xF0:
584 // characters U-00010000 - U-001FFFFF, mask 11110XXX
585 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
586 $utf8 .= substr($chrs, $c, 4);
587 $c += 3;
588 break;
589
590 case ($ord_chrs_c & 0xFC) == 0xF8:
591 // characters U-00200000 - U-03FFFFFF, mask 111110XX
592 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
593 $utf8 .= substr($chrs, $c, 5);
594 $c += 4;
595 break;
596
597 case ($ord_chrs_c & 0xFE) == 0xFC:
598 // characters U-04000000 - U-7FFFFFFF, mask 1111110X
599 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
600 $utf8 .= substr($chrs, $c, 6);
601 $c += 5;
602 break;
603
604 }
605
606 }
607
608 return $utf8;
609
610 } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
611 // array, or object notation
612
613 if ($str{0} == '[') {
614 $stk = array(SERVICES_JSON_IN_ARR);
615 $arr = array();
616 } else {
617 if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
618 $stk = array(SERVICES_JSON_IN_OBJ);
619 $obj = array();
620 } else {
621 $stk = array(SERVICES_JSON_IN_OBJ);
622 $obj = new stdClass();
623 }
624 }
625
626 array_push($stk, array('what' => SERVICES_JSON_SLICE,
627 'where' => 0,
628 'delim' => false));
629
630 $chrs = substr($str, 1, -1);
631 $chrs = $this->reduce_string($chrs);
632
633 if ($chrs == '') {
634 if (reset($stk) == SERVICES_JSON_IN_ARR) {
635 return $arr;
636
637 } else {
638 return $obj;
639
640 }
641 }
642
643 //print("\nparsing {$chrs}\n");
644
645 $strlen_chrs = strlen($chrs);
646
647 for ($c = 0; $c <= $strlen_chrs; ++$c) {
648
649 $top = end($stk);
650 $substr_chrs_c_2 = substr($chrs, $c, 2);
651
652 if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
653 // found a comma that is not inside a string, array, etc.,
654 // OR we've reached the end of the character list
655 $slice = substr($chrs, $top['where'], ($c - $top['where']));
656 array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
657 //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
658
659 if (reset($stk) == SERVICES_JSON_IN_ARR) {
660 // we are in an array, so just push an element onto the stack
661 array_push($arr, $this->decode($slice));
662
663 } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
664 // we are in an object, so figure
665 // out the property name and set an
666 // element in an associative array,
667 // for now
668 $parts = array();
669
670 if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
671 // "name":value pair
672 $key = $this->decode($parts[1]);
673 $val = $this->decode($parts[2]);
674
675 if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
676 $obj[$key] = $val;
677 } else {
678 $obj->$key = $val;
679 }
680 } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
681 // name:value pair, where name is unquoted
682 $key = $parts[1];
683 $val = $this->decode($parts[2]);
684
685 if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
686 $obj[$key] = $val;
687 } else {
688 $obj->$key = $val;
689 }
690 }
691
692 }
693
694 } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
695 // found a quote, and we are not inside a string
696 array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
697 //print("Found start of string at {$c}\n");
698
699 } elseif (($chrs{$c} == $top['delim']) &&
700 ($top['what'] == SERVICES_JSON_IN_STR) &&
701 ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
702 // found a quote, we're in a string, and it's not escaped
703 // we know that it's not escaped becase there is _not_ an
704 // odd number of backslashes at the end of the string so far
705 array_pop($stk);
706 //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
707
708 } elseif (($chrs{$c} == '[') &&
709 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
710 // found a left-bracket, and we are in an array, object, or slice
711 array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
712 //print("Found start of array at {$c}\n");
713
714 } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
715 // found a right-bracket, and we're in an array
716 array_pop($stk);
717 //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
718
719 } elseif (($chrs{$c} == '{') &&
720 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
721 // found a left-brace, and we are in an array, object, or slice
722 array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
723 //print("Found start of object at {$c}\n");
724
725 } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
726 // found a right-brace, and we're in an object
727 array_pop($stk);
728 //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
729
730 } elseif (($substr_chrs_c_2 == '/*') &&
731 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
732 // found a comment start, and we are in an array, object, or slice
733 array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
734 $c++;
735 //print("Found start of comment at {$c}\n");
736
737 } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
738 // found a comment end, and we're in one now
739 array_pop($stk);
740 $c++;
741
742 for ($i = $top['where']; $i <= $c; ++$i)
743 $chrs = substr_replace($chrs, ' ', $i, 1);
744
745 //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
746
747 }
748
749 }
750
751 if (reset($stk) == SERVICES_JSON_IN_ARR) {
752 return $arr;
753
754 } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
755 return $obj;
756
757 }
758
759 }
760 }
761 }
762
763 /**
764 * @todo Ultimately, this should just call PEAR::isError()
765 */
766 function isError($data, $code = null)
767 {
768 if (class_exists('pear')) {
769 return PEAR::isError($data, $code);
770 } elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
771 is_subclass_of($data, 'services_json_error'))) {
772 return true;
773 }
774
775 return false;
776 }
777 }
778
779 if (class_exists('PEAR_Error')) {
780
781 class Services_JSON_Error extends PEAR_Error
782 {
783 function Services_JSON_Error($message = 'unknown error', $code = null,
784 $mode = null, $options = null, $userinfo = null)
785 {
786 parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
787 }
788 }
789
790 } else {
791
792 /**
793 * @todo Ultimately, this class shall be descended from PEAR_Error
794 */
795 class Services_JSON_Error
796 {
797 function Services_JSON_Error($message = 'unknown error', $code = null,
798 $mode = null, $options = null, $userinfo = null)
799 {
800
801 }
802 }
803
804 }
805 ?>
806