|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00010 define('JSON_BOOL', 1); 00011 define('JSON_INT', 2); 00012 define('JSON_STR', 3); 00013 define('JSON_FLOAT', 4); 00014 define('JSON_NULL', 5); 00015 define('JSON_START_OBJ', 6); 00016 define('JSON_END_OBJ', 7); 00017 define('JSON_START_ARRAY', 8); 00018 define('JSON_END_ARRAY', 9); 00019 define('JSON_KEY', 10); 00020 define('JSON_SKIP', 11); 00021 00022 define('JSON_IN_ARRAY', 30); 00023 define('JSON_IN_OBJECT', 40); 00024 define('JSON_IN_BETWEEN', 50); 00025 00026 class Moxiecode_JSONReader { 00027 var $_data, $_len, $_pos; 00028 var $_value, $_token; 00029 var $_location, $_lastLocations; 00030 var $_needProp; 00031 00032 function Moxiecode_JSONReader($data) { 00033 $this->_data = $data; 00034 $this->_len = strlen($data); 00035 $this->_pos = -1; 00036 $this->_location = JSON_IN_BETWEEN; 00037 $this->_lastLocations = array(); 00038 $this->_needProp = false; 00039 } 00040 00041 function getToken() { 00042 return $this->_token; 00043 } 00044 00045 function getLocation() { 00046 return $this->_location; 00047 } 00048 00049 function getTokenName() { 00050 switch ($this->_token) { 00051 case JSON_BOOL: 00052 return 'JSON_BOOL'; 00053 00054 case JSON_INT: 00055 return 'JSON_INT'; 00056 00057 case JSON_STR: 00058 return 'JSON_STR'; 00059 00060 case JSON_FLOAT: 00061 return 'JSON_FLOAT'; 00062 00063 case JSON_NULL: 00064 return 'JSON_NULL'; 00065 00066 case JSON_START_OBJ: 00067 return 'JSON_START_OBJ'; 00068 00069 case JSON_END_OBJ: 00070 return 'JSON_END_OBJ'; 00071 00072 case JSON_START_ARRAY: 00073 return 'JSON_START_ARRAY'; 00074 00075 case JSON_END_ARRAY: 00076 return 'JSON_END_ARRAY'; 00077 00078 case JSON_KEY: 00079 return 'JSON_KEY'; 00080 } 00081 00082 return 'UNKNOWN'; 00083 } 00084 00085 function getValue() { 00086 return $this->_value; 00087 } 00088 00089 function readToken() { 00090 $chr = $this->read(); 00091 00092 if ($chr != null) { 00093 switch ($chr) { 00094 case '[': 00095 $this->_lastLocation[] = $this->_location; 00096 $this->_location = JSON_IN_ARRAY; 00097 $this->_token = JSON_START_ARRAY; 00098 $this->_value = null; 00099 $this->readAway(); 00100 return true; 00101 00102 case ']': 00103 $this->_location = array_pop($this->_lastLocation); 00104 $this->_token = JSON_END_ARRAY; 00105 $this->_value = null; 00106 $this->readAway(); 00107 00108 if ($this->_location == JSON_IN_OBJECT) 00109 $this->_needProp = true; 00110 00111 return true; 00112 00113 case '{': 00114 $this->_lastLocation[] = $this->_location; 00115 $this->_location = JSON_IN_OBJECT; 00116 $this->_needProp = true; 00117 $this->_token = JSON_START_OBJ; 00118 $this->_value = null; 00119 $this->readAway(); 00120 return true; 00121 00122 case '}': 00123 $this->_location = array_pop($this->_lastLocation); 00124 $this->_token = JSON_END_OBJ; 00125 $this->_value = null; 00126 $this->readAway(); 00127 00128 if ($this->_location == JSON_IN_OBJECT) 00129 $this->_needProp = true; 00130 00131 return true; 00132 00133 // String 00134 case '"': 00135 case '\'': 00136 return $this->_readString($chr); 00137 00138 // Null 00139 case 'n': 00140 return $this->_readNull(); 00141 00142 // Bool 00143 case 't': 00144 case 'f': 00145 return $this->_readBool($chr); 00146 00147 default: 00148 // Is number 00149 if (is_numeric($chr) || $chr == '-' || $chr == '.') 00150 return $this->_readNumber($chr); 00151 00152 return true; 00153 } 00154 } 00155 00156 return false; 00157 } 00158 00159 function _readBool($chr) { 00160 $this->_token = JSON_BOOL; 00161 $this->_value = $chr == 't'; 00162 00163 if ($chr == 't') 00164 $this->skip(3); // rue 00165 else 00166 $this->skip(4); // alse 00167 00168 $this->readAway(); 00169 00170 if ($this->_location == JSON_IN_OBJECT && !$this->_needProp) 00171 $this->_needProp = true; 00172 00173 return true; 00174 } 00175 00176 function _readNull() { 00177 $this->_token = JSON_NULL; 00178 $this->_value = null; 00179 00180 $this->skip(3); // ull 00181 $this->readAway(); 00182 00183 if ($this->_location == JSON_IN_OBJECT && !$this->_needProp) 00184 $this->_needProp = true; 00185 00186 return true; 00187 } 00188 00189 function _readString($quote) { 00190 $output = ""; 00191 $this->_token = JSON_STR; 00192 $endString = false; 00193 00194 while (($chr = $this->peek()) != -1) { 00195 switch ($chr) { 00196 case '\\': 00197 // Read away slash 00198 $this->read(); 00199 00200 // Read escape code 00201 $chr = $this->read(); 00202 switch ($chr) { 00203 case 't': 00204 $output .= "\t"; 00205 break; 00206 00207 case 'b': 00208 $output .= "\b"; 00209 break; 00210 00211 case 'f': 00212 $output .= "\f"; 00213 break; 00214 00215 case 'r': 00216 $output .= "\r"; 00217 break; 00218 00219 case 'n': 00220 $output .= "\n"; 00221 break; 00222 00223 case 'u': 00224 $output .= $this->_int2utf8(hexdec($this->read(4))); 00225 break; 00226 00227 default: 00228 $output .= $chr; 00229 break; 00230 } 00231 00232 break; 00233 00234 case '\'': 00235 case '"': 00236 if ($chr == $quote) 00237 $endString = true; 00238 00239 $chr = $this->read(); 00240 if ($chr != -1 && $chr != $quote) 00241 $output .= $chr; 00242 00243 break; 00244 00245 default: 00246 $output .= $this->read(); 00247 } 00248 00249 // String terminated 00250 if ($endString) 00251 break; 00252 } 00253 00254 $this->readAway(); 00255 $this->_value = $output; 00256 00257 // Needed a property 00258 if ($this->_needProp) { 00259 $this->_token = JSON_KEY; 00260 $this->_needProp = false; 00261 return true; 00262 } 00263 00264 if ($this->_location == JSON_IN_OBJECT && !$this->_needProp) 00265 $this->_needProp = true; 00266 00267 return true; 00268 } 00269 00270 function _int2utf8($int) { 00271 $int = intval($int); 00272 00273 switch ($int) { 00274 case 0: 00275 return chr(0); 00276 00277 case ($int & 0x7F): 00278 return chr($int); 00279 00280 case ($int & 0x7FF): 00281 return chr(0xC0 | (($int >> 6) & 0x1F)) . chr(0x80 | ($int & 0x3F)); 00282 00283 case ($int & 0xFFFF): 00284 return chr(0xE0 | (($int >> 12) & 0x0F)) . chr(0x80 | (($int >> 6) & 0x3F)) . chr (0x80 | ($int & 0x3F)); 00285 00286 case ($int & 0x1FFFFF): 00287 return chr(0xF0 | ($int >> 18)) . chr(0x80 | (($int >> 12) & 0x3F)) . chr(0x80 | (($int >> 6) & 0x3F)) . chr(0x80 | ($int & 0x3F)); 00288 } 00289 } 00290 00291 function _readNumber($start) { 00292 $value = ""; 00293 $isFloat = false; 00294 00295 $this->_token = JSON_INT; 00296 $value .= $start; 00297 00298 while (($chr = $this->peek()) != -1) { 00299 if (is_numeric($chr) || $chr == '-' || $chr == '.') { 00300 if ($chr == '.') 00301 $isFloat = true; 00302 00303 $value .= $this->read(); 00304 } else 00305 break; 00306 } 00307 00308 $this->readAway(); 00309 00310 if ($isFloat) { 00311 $this->_token = JSON_FLOAT; 00312 $this->_value = floatval($value); 00313 } else 00314 $this->_value = intval($value); 00315 00316 if ($this->_location == JSON_IN_OBJECT && !$this->_needProp) 00317 $this->_needProp = true; 00318 00319 return true; 00320 } 00321 00322 function readAway() { 00323 while (($chr = $this->peek()) != null) { 00324 if ($chr != ':' && $chr != ',' && $chr != ' ') 00325 return; 00326 00327 $this->read(); 00328 } 00329 } 00330 00331 function read($len = 1) { 00332 if ($this->_pos < $this->_len) { 00333 if ($len > 1) { 00334 $str = substr($this->_data, $this->_pos + 1, $len); 00335 $this->_pos += $len; 00336 00337 return $str; 00338 } else 00339 return $this->_data[++$this->_pos]; 00340 } 00341 00342 return null; 00343 } 00344 00345 function skip($len) { 00346 $this->_pos += $len; 00347 } 00348 00349 function peek() { 00350 if ($this->_pos < $this->_len) 00351 return $this->_data[$this->_pos + 1]; 00352 00353 return null; 00354 } 00355 } 00356 00362 class Moxiecode_JSON { 00363 function Moxiecode_JSON() { 00364 } 00365 00366 function decode($input) { 00367 $reader = new Moxiecode_JSONReader($input); 00368 00369 return $this->readValue($reader); 00370 } 00371 00372 function readValue(&$reader) { 00373 $this->data = array(); 00374 $this->parents = array(); 00375 $this->cur =& $this->data; 00376 $key = null; 00377 $loc = JSON_IN_ARRAY; 00378 00379 while ($reader->readToken()) { 00380 switch ($reader->getToken()) { 00381 case JSON_STR: 00382 case JSON_INT: 00383 case JSON_BOOL: 00384 case JSON_FLOAT: 00385 case JSON_NULL: 00386 switch ($reader->getLocation()) { 00387 case JSON_IN_OBJECT: 00388 $this->cur[$key] = $reader->getValue(); 00389 break; 00390 00391 case JSON_IN_ARRAY: 00392 $this->cur[] = $reader->getValue(); 00393 break; 00394 00395 default: 00396 return $reader->getValue(); 00397 } 00398 break; 00399 00400 case JSON_KEY: 00401 $key = $reader->getValue(); 00402 break; 00403 00404 case JSON_START_OBJ: 00405 case JSON_START_ARRAY: 00406 if ($loc == JSON_IN_OBJECT) 00407 $this->addArray($key); 00408 else 00409 $this->addArray(null); 00410 00411 $cur =& $obj; 00412 00413 $loc = $reader->getLocation(); 00414 break; 00415 00416 case JSON_END_OBJ: 00417 case JSON_END_ARRAY: 00418 $loc = $reader->getLocation(); 00419 00420 if (count($this->parents) > 0) { 00421 $this->cur =& $this->parents[count($this->parents) - 1]; 00422 array_pop($this->parents); 00423 } 00424 break; 00425 } 00426 } 00427 00428 return $this->data[0]; 00429 } 00430 00431 // This method was needed since PHP is crapy and doesn't have pointers/references 00432 function addArray($key) { 00433 $this->parents[] =& $this->cur; 00434 $ar = array(); 00435 00436 if ($key) 00437 $this->cur[$key] =& $ar; 00438 else 00439 $this->cur[] =& $ar; 00440 00441 $this->cur =& $ar; 00442 } 00443 00444 function getDelim($index, &$reader) { 00445 switch ($reader->getLocation()) { 00446 case JSON_IN_ARRAY: 00447 case JSON_IN_OBJECT: 00448 if ($index > 0) 00449 return ","; 00450 break; 00451 } 00452 00453 return ""; 00454 } 00455 00456 function encode($input) { 00457 switch (gettype($input)) { 00458 case 'boolean': 00459 return $input ? 'true' : 'false'; 00460 00461 case 'integer': 00462 return (int) $input; 00463 00464 case 'float': 00465 case 'double': 00466 return (float) $input; 00467 00468 case 'NULL': 00469 return 'null'; 00470 00471 case 'string': 00472 return $this->encodeString($input); 00473 00474 case 'array': 00475 return $this->_encodeArray($input); 00476 00477 case 'object': 00478 return $this->_encodeArray(get_object_vars($input)); 00479 } 00480 00481 return ''; 00482 } 00483 00484 function encodeString($input) { 00485 // Needs to be escaped 00486 if (preg_match('/[^a-zA-Z0-9]/', $input)) { 00487 $output = ''; 00488 00489 for ($i=0; $i<strlen($input); $i++) { 00490 switch ($input[$i]) { 00491 case "\b": 00492 $output .= "\\b"; 00493 break; 00494 00495 case "\t": 00496 $output .= "\\t"; 00497 break; 00498 00499 case "\f": 00500 $output .= "\\f"; 00501 break; 00502 00503 case "\r": 00504 $output .= "\\r"; 00505 break; 00506 00507 case "\n": 00508 $output .= "\\n"; 00509 break; 00510 00511 case '\\': 00512 $output .= "\\\\"; 00513 break; 00514 00515 case '\'': 00516 $output .= "\\'"; 00517 break; 00518 00519 case '"': 00520 $output .= '\"'; 00521 break; 00522 00523 default: 00524 $byte = ord($input[$i]); 00525 00526 if (($byte & 0xE0) == 0xC0) { 00527 $char = pack('C*', $byte, ord($input[$i + 1])); 00528 $i += 1; 00529 $output .= sprintf('\u%04s', bin2hex($this->_utf82utf16($char))); 00530 } if (($byte & 0xF0) == 0xE0) { 00531 $char = pack('C*', $byte, ord($input[$i + 1]), ord($input[$i + 2])); 00532 $i += 2; 00533 $output .= sprintf('\u%04s', bin2hex($this->_utf82utf16($char))); 00534 } if (($byte & 0xF8) == 0xF0) { 00535 $char = pack('C*', $byte, ord($input[$i + 1]), ord($input[$i + 2]), ord($input[$i + 3])); 00536 $i += 3; 00537 $output .= sprintf('\u%04s', bin2hex($this->_utf82utf16($char))); 00538 } if (($byte & 0xFC) == 0xF8) { 00539 $char = pack('C*', $byte, ord($input[$i + 1]), ord($input[$i + 2]), ord($input[$i + 3]), ord($input[$i + 4])); 00540 $i += 4; 00541 $output .= sprintf('\u%04s', bin2hex($this->_utf82utf16($char))); 00542 } if (($byte & 0xFE) == 0xFC) { 00543 $char = pack('C*', $byte, ord($input[$i + 1]), ord($input[$i + 2]), ord($input[$i + 3]), ord($input[$i + 4]), ord($input[$i + 5])); 00544 $i += 5; 00545 $output .= sprintf('\u%04s', bin2hex($this->_utf82utf16($char))); 00546 } else if ($byte < 128) 00547 $output .= $input[$i]; 00548 } 00549 } 00550 00551 return '"' . $output . '"'; 00552 } 00553 00554 return '"' . $input . '"'; 00555 } 00556 00557 function _utf82utf16($utf8) { 00558 if (function_exists('mb_convert_encoding')) 00559 return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); 00560 00561 switch (strlen($utf8)) { 00562 case 1: 00563 return $utf8; 00564 00565 case 2: 00566 return chr(0x07 & (ord($utf8[0]) >> 2)) . chr((0xC0 & (ord($utf8[0]) << 6)) | (0x3F & ord($utf8[1]))); 00567 00568 case 3: 00569 return chr((0xF0 & (ord($utf8[0]) << 4)) | (0x0F & (ord($utf8[1]) >> 2))) . chr((0xC0 & (ord($utf8[1]) << 6)) | (0x7F & ord($utf8[2]))); 00570 } 00571 00572 return ''; 00573 } 00574 00575 function _encodeArray($input) { 00576 $output = ''; 00577 $isIndexed = true; 00578 00579 $keys = array_keys($input); 00580 for ($i=0; $i<count($keys); $i++) { 00581 if (!is_int($keys[$i])) { 00582 $output .= $this->encodeString($keys[$i]) . ':' . $this->encode($input[$keys[$i]]); 00583 $isIndexed = false; 00584 } else 00585 $output .= $this->encode($input[$keys[$i]]); 00586 00587 if ($i != count($keys) - 1) 00588 $output .= ','; 00589 } 00590 00591 return $isIndexed ? '[' . $output . ']' : '{' . $output . '}'; 00592 } 00593 } 00594 00595 ?>