PageRenderTime 52ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/framework/Web/Javascripts/TJSON.php

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