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