1 <?php 2 /** 3 * $Id: JSON.php 40 2007-06-18 11:43:15Z spocke $ 4 * 5 * @package MCManager.utils 6 * @author Moxiecode 7 * @copyright Copyright © 2007, Moxiecode Systems AB, All rights reserved. 8 */ 9 10 define('JSON_BOOL', 1); 11 define('JSON_INT', 2); 12 define('JSON_STR', 3); 13 define('JSON_FLOAT', 4); 14 define('JSON_NULL', 5); 15 define('JSON_START_OBJ', 6); 16 define('JSON_END_OBJ', 7); 17 define('JSON_START_ARRAY', 8); 18 define('JSON_END_ARRAY', 9); 19 define('JSON_KEY', 10); 20 define('JSON_SKIP', 11); 21 22 define('JSON_IN_ARRAY', 30); 23 define('JSON_IN_OBJECT', 40); 24 define('JSON_IN_BETWEEN', 50); 25 26 class Moxiecode_JSONReader { 27 var $_data, $_len, $_pos; 28 var $_value, $_token; 29 var $_location, $_lastLocations; 30 var $_needProp; 31 32 public function __construct($data) { 33 $this->_data = $data; 34 $this->_len = strlen($data); 35 $this->_pos = -1; 36 $this->_location = JSON_IN_BETWEEN; 37 $this->_lastLocations = array(); 38 $this->_needProp = false; 39 } 40 41 /** 42 * Old syntax of class constructor. Deprecated in PHP7. 43 * 44 * @deprecated since Moodle 3.1 45 */ 46 public function Moxiecode_JSONReader($data) { 47 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER); 48 self::__construct($data); 49 } 50 51 function getToken() { 52 return $this->_token; 53 } 54 55 function getLocation() { 56 return $this->_location; 57 } 58 59 function getTokenName() { 60 switch ($this->_token) { 61 case JSON_BOOL: 62 return 'JSON_BOOL'; 63 64 case JSON_INT: 65 return 'JSON_INT'; 66 67 case JSON_STR: 68 return 'JSON_STR'; 69 70 case JSON_FLOAT: 71 return 'JSON_FLOAT'; 72 73 case JSON_NULL: 74 return 'JSON_NULL'; 75 76 case JSON_START_OBJ: 77 return 'JSON_START_OBJ'; 78 79 case JSON_END_OBJ: 80 return 'JSON_END_OBJ'; 81 82 case JSON_START_ARRAY: 83 return 'JSON_START_ARRAY'; 84 85 case JSON_END_ARRAY: 86 return 'JSON_END_ARRAY'; 87 88 case JSON_KEY: 89 return 'JSON_KEY'; 90 } 91 92 return 'UNKNOWN'; 93 } 94 95 function getValue() { 96 return $this->_value; 97 } 98 99 function readToken() { 100 $chr = $this->read(); 101 102 if ($chr != null) { 103 switch ($chr) { 104 case '[': 105 $this->_lastLocation[] = $this->_location; 106 $this->_location = JSON_IN_ARRAY; 107 $this->_token = JSON_START_ARRAY; 108 $this->_value = null; 109 $this->readAway(); 110 return true; 111 112 case ']': 113 $this->_location = array_pop($this->_lastLocation); 114 $this->_token = JSON_END_ARRAY; 115 $this->_value = null; 116 $this->readAway(); 117 118 if ($this->_location == JSON_IN_OBJECT) 119 $this->_needProp = true; 120 121 return true; 122 123 case '{': 124 $this->_lastLocation[] = $this->_location; 125 $this->_location = JSON_IN_OBJECT; 126 $this->_needProp = true; 127 $this->_token = JSON_START_OBJ; 128 $this->_value = null; 129 $this->readAway(); 130 return true; 131 132 case '}': 133 $this->_location = array_pop($this->_lastLocation); 134 $this->_token = JSON_END_OBJ; 135 $this->_value = null; 136 $this->readAway(); 137 138 if ($this->_location == JSON_IN_OBJECT) 139 $this->_needProp = true; 140 141 return true; 142 143 // String 144 case '"': 145 case '\'': 146 return $this->_readString($chr); 147 148 // Null 149 case 'n': 150 return $this->_readNull(); 151 152 // Bool 153 case 't': 154 case 'f': 155 return $this->_readBool($chr); 156 157 default: 158 // Is number 159 if (is_numeric($chr) || $chr == '-' || $chr == '.') 160 return $this->_readNumber($chr); 161 162 return true; 163 } 164 } 165 166 return false; 167 } 168 169 function _readBool($chr) { 170 $this->_token = JSON_BOOL; 171 $this->_value = $chr == 't'; 172 173 if ($chr == 't') 174 $this->skip(3); // rue 175 else 176 $this->skip(4); // alse 177 178 $this->readAway(); 179 180 if ($this->_location == JSON_IN_OBJECT && !$this->_needProp) 181 $this->_needProp = true; 182 183 return true; 184 } 185 186 function _readNull() { 187 $this->_token = JSON_NULL; 188 $this->_value = null; 189 190 $this->skip(3); // ull 191 $this->readAway(); 192 193 if ($this->_location == JSON_IN_OBJECT && !$this->_needProp) 194 $this->_needProp = true; 195 196 return true; 197 } 198 199 function _readString($quote) { 200 $output = ""; 201 $this->_token = JSON_STR; 202 $endString = false; 203 204 while (($chr = $this->peek()) != -1) { 205 switch ($chr) { 206 case '\\': 207 // Read away slash 208 $this->read(); 209 210 // Read escape code 211 $chr = $this->read(); 212 switch ($chr) { 213 case 't': 214 $output .= "\t"; 215 break; 216 217 case 'b': 218 $output .= "\b"; 219 break; 220 221 case 'f': 222 $output .= "\f"; 223 break; 224 225 case 'r': 226 $output .= "\r"; 227 break; 228 229 case 'n': 230 $output .= "\n"; 231 break; 232 233 case 'u': 234 $output .= $this->_int2utf8(hexdec($this->read(4))); 235 break; 236 237 default: 238 $output .= $chr; 239 break; 240 } 241 242 break; 243 244 case '\'': 245 case '"': 246 if ($chr == $quote) 247 $endString = true; 248 249 $chr = $this->read(); 250 if ($chr != -1 && $chr != $quote) 251 $output .= $chr; 252 253 break; 254 255 default: 256 $output .= $this->read(); 257 } 258 259 // String terminated 260 if ($endString) 261 break; 262 } 263 264 $this->readAway(); 265 $this->_value = $output; 266 267 // Needed a property 268 if ($this->_needProp) { 269 $this->_token = JSON_KEY; 270 $this->_needProp = false; 271 return true; 272 } 273 274 if ($this->_location == JSON_IN_OBJECT && !$this->_needProp) 275 $this->_needProp = true; 276 277 return true; 278 } 279 280 function _int2utf8($int) { 281 $int = intval($int); 282 283 switch ($int) { 284 case 0: 285 return chr(0); 286 287 case ($int & 0x7F): 288 return chr($int); 289 290 case ($int & 0x7FF): 291 return chr(0xC0 | (($int >> 6) & 0x1F)) . chr(0x80 | ($int & 0x3F)); 292 293 case ($int & 0xFFFF): 294 return chr(0xE0 | (($int >> 12) & 0x0F)) . chr(0x80 | (($int >> 6) & 0x3F)) . chr (0x80 | ($int & 0x3F)); 295 296 case ($int & 0x1FFFFF): 297 return chr(0xF0 | ($int >> 18)) . chr(0x80 | (($int >> 12) & 0x3F)) . chr(0x80 | (($int >> 6) & 0x3F)) . chr(0x80 | ($int & 0x3F)); 298 } 299 } 300 301 function _readNumber($start) { 302 $value = ""; 303 $isFloat = false; 304 305 $this->_token = JSON_INT; 306 $value .= $start; 307 308 while (($chr = $this->peek()) != -1) { 309 if (is_numeric($chr) || $chr == '-' || $chr == '.') { 310 if ($chr == '.') 311 $isFloat = true; 312 313 $value .= $this->read(); 314 } else 315 break; 316 } 317 318 $this->readAway(); 319 320 if ($isFloat) { 321 $this->_token = JSON_FLOAT; 322 $this->_value = floatval($value); 323 } else 324 $this->_value = intval($value); 325 326 if ($this->_location == JSON_IN_OBJECT && !$this->_needProp) 327 $this->_needProp = true; 328 329 return true; 330 } 331 332 function readAway() { 333 while (($chr = $this->peek()) != null) { 334 if ($chr != ':' && $chr != ',' && $chr != ' ') 335 return; 336 337 $this->read(); 338 } 339 } 340 341 function read($len = 1) { 342 if ($this->_pos < $this->_len) { 343 if ($len > 1) { 344 $str = substr($this->_data, $this->_pos + 1, $len); 345 $this->_pos += $len; 346 347 return $str; 348 } else 349 return $this->_data[++$this->_pos]; 350 } 351 352 return null; 353 } 354 355 function skip($len) { 356 $this->_pos += $len; 357 } 358 359 function peek() { 360 if ($this->_pos < $this->_len) 361 return $this->_data[$this->_pos + 1]; 362 363 return null; 364 } 365 } 366 367 /** 368 * This class handles JSON stuff. 369 * 370 * @package MCManager.utils 371 */ 372 class Moxiecode_JSON { 373 public function __construct() { 374 } 375 376 /** 377 * Old syntax of class constructor. Deprecated in PHP7. 378 * 379 * @deprecated since Moodle 3.1 380 */ 381 public function Moxiecode_JSON() { 382 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER); 383 self::__construct(); 384 } 385 386 function decode($input) { 387 $reader = new Moxiecode_JSONReader($input); 388 389 return $this->readValue($reader); 390 } 391 392 function readValue(&$reader) { 393 $this->data = array(); 394 $this->parents = array(); 395 $this->cur =& $this->data; 396 $key = null; 397 $loc = JSON_IN_ARRAY; 398 399 while ($reader->readToken()) { 400 switch ($reader->getToken()) { 401 case JSON_STR: 402 case JSON_INT: 403 case JSON_BOOL: 404 case JSON_FLOAT: 405 case JSON_NULL: 406 switch ($reader->getLocation()) { 407 case JSON_IN_OBJECT: 408 $this->cur[$key] = $reader->getValue(); 409 break; 410 411 case JSON_IN_ARRAY: 412 $this->cur[] = $reader->getValue(); 413 break; 414 415 default: 416 return $reader->getValue(); 417 } 418 break; 419 420 case JSON_KEY: 421 $key = $reader->getValue(); 422 break; 423 424 case JSON_START_OBJ: 425 case JSON_START_ARRAY: 426 if ($loc == JSON_IN_OBJECT) 427 $this->addArray($key); 428 else 429 $this->addArray(null); 430 431 $cur =& $obj; 432 433 $loc = $reader->getLocation(); 434 break; 435 436 case JSON_END_OBJ: 437 case JSON_END_ARRAY: 438 $loc = $reader->getLocation(); 439 440 if (count($this->parents) > 0) { 441 $this->cur =& $this->parents[count($this->parents) - 1]; 442 array_pop($this->parents); 443 } 444 break; 445 } 446 } 447 448 return $this->data[0]; 449 } 450 451 // This method was needed since PHP is crapy and doesn't have pointers/references 452 function addArray($key) { 453 $this->parents[] =& $this->cur; 454 $ar = array(); 455 456 if ($key) 457 $this->cur[$key] =& $ar; 458 else 459 $this->cur[] =& $ar; 460 461 $this->cur =& $ar; 462 } 463 464 function getDelim($index, &$reader) { 465 switch ($reader->getLocation()) { 466 case JSON_IN_ARRAY: 467 case JSON_IN_OBJECT: 468 if ($index > 0) 469 return ","; 470 break; 471 } 472 473 return ""; 474 } 475 476 function encode($input) { 477 switch (gettype($input)) { 478 case 'boolean': 479 return $input ? 'true' : 'false'; 480 481 case 'integer': 482 return (int) $input; 483 484 case 'float': 485 case 'double': 486 return (float) $input; 487 488 case 'NULL': 489 return 'null'; 490 491 case 'string': 492 return $this->encodeString($input); 493 494 case 'array': 495 return $this->_encodeArray($input); 496 497 case 'object': 498 return $this->_encodeArray(get_object_vars($input)); 499 } 500 501 return ''; 502 } 503 504 function encodeString($input) { 505 // Needs to be escaped 506 if (preg_match('/[^a-zA-Z0-9]/', $input)) { 507 $output = ''; 508 509 for ($i=0; $i<strlen($input); $i++) { 510 switch ($input[$i]) { 511 case "\b": 512 $output .= "\\b"; 513 break; 514 515 case "\t": 516 $output .= "\\t"; 517 break; 518 519 case "\f": 520 $output .= "\\f"; 521 break; 522 523 case "\r": 524 $output .= "\\r"; 525 break; 526 527 case "\n": 528 $output .= "\\n"; 529 break; 530 531 case '\\': 532 $output .= "\\\\"; 533 break; 534 535 case '\'': 536 $output .= "\\'"; 537 break; 538 539 case '"': 540 $output .= '\"'; 541 break; 542 543 default: 544 $byte = ord($input[$i]); 545 546 if (($byte & 0xE0) == 0xC0) { 547 $char = pack('C*', $byte, ord($input[$i + 1])); 548 $i += 1; 549 $output .= sprintf('\u%04s', bin2hex($this->_utf82utf16($char))); 550 } if (($byte & 0xF0) == 0xE0) { 551 $char = pack('C*', $byte, ord($input[$i + 1]), ord($input[$i + 2])); 552 $i += 2; 553 $output .= sprintf('\u%04s', bin2hex($this->_utf82utf16($char))); 554 } if (($byte & 0xF8) == 0xF0) { 555 $char = pack('C*', $byte, ord($input[$i + 1]), ord($input[$i + 2]), ord($input[$i + 3])); 556 $i += 3; 557 $output .= sprintf('\u%04s', bin2hex($this->_utf82utf16($char))); 558 } if (($byte & 0xFC) == 0xF8) { 559 $char = pack('C*', $byte, ord($input[$i + 1]), ord($input[$i + 2]), ord($input[$i + 3]), ord($input[$i + 4])); 560 $i += 4; 561 $output .= sprintf('\u%04s', bin2hex($this->_utf82utf16($char))); 562 } if (($byte & 0xFE) == 0xFC) { 563 $char = pack('C*', $byte, ord($input[$i + 1]), ord($input[$i + 2]), ord($input[$i + 3]), ord($input[$i + 4]), ord($input[$i + 5])); 564 $i += 5; 565 $output .= sprintf('\u%04s', bin2hex($this->_utf82utf16($char))); 566 } else if ($byte < 128) 567 $output .= $input[$i]; 568 } 569 } 570 571 return '"' . $output . '"'; 572 } 573 574 return '"' . $input . '"'; 575 } 576 577 function _utf82utf16($utf8) { 578 if (function_exists('mb_convert_encoding')) 579 return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); 580 581 switch (strlen($utf8)) { 582 case 1: 583 return $utf8; 584 585 case 2: 586 return chr(0x07 & (ord($utf8[0]) >> 2)) . chr((0xC0 & (ord($utf8[0]) << 6)) | (0x3F & ord($utf8[1]))); 587 588 case 3: 589 return chr((0xF0 & (ord($utf8[0]) << 4)) | (0x0F & (ord($utf8[1]) >> 2))) . chr((0xC0 & (ord($utf8[1]) << 6)) | (0x7F & ord($utf8[2]))); 590 } 591 592 return ''; 593 } 594 595 function _encodeArray($input) { 596 $output = ''; 597 $isIndexed = true; 598 599 $keys = array_keys($input); 600 for ($i=0; $i<count($keys); $i++) { 601 if (!is_int($keys[$i])) { 602 $output .= $this->encodeString($keys[$i]) . ':' . $this->encode($input[$keys[$i]]); 603 $isIndexed = false; 604 } else 605 $output .= $this->encode($input[$keys[$i]]); 606 607 if ($i != count($keys) - 1) 608 $output .= ','; 609 } 610 611 return $isIndexed ? '[' . $output . ']' : '{' . $output . '}'; 612 } 613 } 614 615 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body