diff options
Diffstat (limited to 'ext/json')
-rw-r--r-- | ext/json/JSON_parser.c | 757 | ||||
-rw-r--r-- | ext/json/JSON_parser.h | 8 | ||||
-rw-r--r-- | ext/json/README | 76 | ||||
-rw-r--r-- | ext/json/config.m4 | 88 | ||||
-rw-r--r-- | ext/json/config.w32 | 10 | ||||
-rw-r--r-- | ext/json/json.c | 449 | ||||
-rw-r--r-- | ext/json/json.dsp | 135 | ||||
-rw-r--r-- | ext/json/package.xml | 152 | ||||
-rw-r--r-- | ext/json/php_json.h | 60 | ||||
-rw-r--r-- | ext/json/tests/fail001.phpt | 166 | ||||
-rw-r--r-- | ext/json/tests/pass001.1.phpt | 895 | ||||
-rw-r--r-- | ext/json/tests/pass001.phpt | 709 | ||||
-rw-r--r-- | ext/json/tests/pass002.phpt | 276 | ||||
-rw-r--r-- | ext/json/tests/pass003.phpt | 95 | ||||
-rw-r--r-- | ext/json/utf8_decode.c | 179 | ||||
-rw-r--r-- | ext/json/utf8_decode.h | 18 | ||||
-rw-r--r-- | ext/json/utf8_to_utf16.c | 56 | ||||
-rw-r--r-- | ext/json/utf8_to_utf16.h | 3 |
18 files changed, 4132 insertions, 0 deletions
diff --git a/ext/json/JSON_parser.c b/ext/json/JSON_parser.c new file mode 100644 index 0000000000..adb974346e --- /dev/null +++ b/ext/json/JSON_parser.c @@ -0,0 +1,757 @@ +/* JSON_parser.c */ + +/* 2005-12-30 */ + +/* +Copyright (c) 2005 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + + +#include "JSON_parser.h" +#include <stdio.h> + +#define true 1 +#define false 0 + +/* + Characters are mapped into these 32 symbol classes. This allows for + significant reductions in the size of the state transition table. +*/ + +/* error */ +#define S_ERR -1 + +/* space */ +#define S_SPA 0 + +/* other whitespace */ +#define S_WSP 1 + +/* { */ +#define S_LBE 2 + +/* } */ +#define S_RBE 3 + +/* [ */ +#define S_LBT 4 + +/* ] */ +#define S_RBT 5 + +/* : */ +#define S_COL 6 + +/* , */ +#define S_COM 7 + +/* " */ +#define S_QUO 8 + +/* \ */ +#define S_BAC 9 + +/* / */ +#define S_SLA 10 + +/* + */ +#define S_PLU 11 + +/* - */ +#define S_MIN 12 + +/* . */ +#define S_DOT 13 + +/* 0 */ +#define S_ZER 14 + +/* 123456789 */ +#define S_DIG 15 + +/* a */ +#define S__A_ 16 + +/* b */ +#define S__B_ 17 + +/* c */ +#define S__C_ 18 + +/* d */ +#define S__D_ 19 + +/* e */ +#define S__E_ 20 + +/* f */ +#define S__F_ 21 + +/* l */ +#define S__L_ 22 + +/* n */ +#define S__N_ 23 + +/* r */ +#define S__R_ 24 + +/* s */ +#define S__S_ 25 + +/* t */ +#define S__T_ 26 + +/* u */ +#define S__U_ 27 + +/* ABCDF */ +#define S_A_F 28 + +/* E */ +#define S_E 29 + +/* everything else */ +#define S_ETC 30 + + +/* + This table maps the 128 ASCII characters into the 32 character classes. + The remaining Unicode characters should be mapped to S_ETC. +*/ +static int ascii_class[128] = { + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_WSP, S_WSP, S_ERR, S_ERR, S_WSP, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + + S_SPA, S_ETC, S_QUO, S_ETC, S_ETC, S_ETC, S_ETC, S_ETC, + S_ETC, S_ETC, S_ETC, S_PLU, S_COM, S_MIN, S_DOT, S_SLA, + S_ZER, S_DIG, S_DIG, S_DIG, S_DIG, S_DIG, S_DIG, S_DIG, + S_DIG, S_DIG, S_COL, S_ETC, S_ETC, S_ETC, S_ETC, S_ETC, + + S_ETC, S_A_F, S_A_F, S_A_F, S_A_F, S_E , S_A_F, S_ETC, + S_ETC, S_ETC, S_ETC, S_ETC, S_ETC, S_ETC, S_ETC, S_ETC, + S_ETC, S_ETC, S_ETC, S_ETC, S_ETC, S_ETC, S_ETC, S_ETC, + S_ETC, S_ETC, S_ETC, S_LBT, S_BAC, S_RBT, S_ETC, S_ETC, + + S_ETC, S__A_, S__B_, S__C_, S__D_, S__E_, S__F_, S_ETC, + S_ETC, S_ETC, S_ETC, S_ETC, S__L_, S_ETC, S__N_, S_ETC, + S_ETC, S_ETC, S__R_, S__S_, S__T_, S__U_, S_ETC, S_ETC, + S_ETC, S_ETC, S_ETC, S_LBE, S_ETC, S_RBE, S_ETC, S_ETC +}; + + +/* + The state transition table takes the current state and the current symbol, + and returns either a new state or an action. A new state is a number between + 0 and 29. An action is a negative number between -1 and -9. A JSON text is + accepted if the end of the text is in state 9 and mode is MODE_DONE. +*/ +static int state_transition_table[30][31] = { +/* 0*/ { 0, 0,-8,-1,-6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +/* 1*/ { 1, 1,-1,-9,-1,-1,-1,-1, 3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +/* 2*/ { 2, 2,-8,-1,-6,-5,-1,-1, 3,-1,-1,-1,20,-1,21,22,-1,-1,-1,-1,-1,13,-1,17,-1,-1,10,-1,-1,-1,-1}, +/* 3*/ { 3,-1, 3, 3, 3, 3, 3, 3,-4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, +/* 4*/ {-1,-1,-1,-1,-1,-1,-1,-1, 3, 3, 3,-1,-1,-1,-1,-1,-1, 3,-1,-1,-1, 3,-1, 3, 3,-1, 3, 5,-1,-1,-1}, +/* 5*/ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 6, 6, 6, 6, 6, 6, 6, 6,-1,-1,-1,-1,-1,-1, 6, 6,-1}, +/* 6*/ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 7, 7, 7, 7, 7, 7, 7, 7,-1,-1,-1,-1,-1,-1, 7, 7,-1}, +/* 7*/ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 8, 8, 8, 8, 8, 8, 8, 8,-1,-1,-1,-1,-1,-1, 8, 8,-1}, +/* 8*/ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 3, 3, 3, 3, 3, 3, 3, 3,-1,-1,-1,-1,-1,-1, 3, 3,-1}, +/* 9*/ { 9, 9,-1,-7,-1,-5,-1,-3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +/*10*/ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,11,-1,-1,-1,-1,-1,-1}, +/*11*/ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,12,-1,-1,-1}, +/*12*/ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 9,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +/*13*/ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,14,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +/*14*/ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,15,-1,-1,-1,-1,-1,-1,-1,-1}, +/*15*/ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,16,-1,-1,-1,-1,-1}, +/*16*/ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 9,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +/*17*/ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,18,-1,-1,-1}, +/*18*/ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,19,-1,-1,-1,-1,-1,-1,-1,-1}, +/*19*/ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 9,-1,-1,-1,-1,-1,-1,-1,-1}, +/*20*/ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,21,22,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +/*21*/ { 9, 9,-1,-7,-1,-5,-1,-3,-1,-1,-1,-1,-1,23,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +/*22*/ { 9, 9,-1,-7,-1,-5,-1,-3,-1,-1,-1,-1,-1,23,22,22,-1,-1,-1,-1,24,-1,-1,-1,-1,-1,-1,-1,-1,24,-1}, +/*23*/ { 9, 9,-1,-7,-1,-5,-1,-3,-1,-1,-1,-1,-1,-1,23,23,-1,-1,-1,-1,24,-1,-1,-1,-1,-1,-1,-1,-1,24,-1}, +/*24*/ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,25,25,-1,26,26,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +/*25*/ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,26,26,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +/*26*/ { 9, 9,-1,-7,-1,-5,-1,-3,-1,-1,-1,-1,-1,-1,26,26,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +/*27*/ {27,27,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +/*28*/ {28,28,-8,-1,-6,-1,-1,-1, 3,-1,-1,-1,20,-1,21,22,-1,-1,-1,-1,-1,13,-1,17,-1,-1,10,-1,-1,-1,-1}, +/*29*/ {29,29,-1,-1,-1,-1,-1,-1, 3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1} +}; + +#define JSON_PARSER_MAX_DEPTH 20 + + +/* + A stack maintains the states of nested structures. +*/ + +typedef struct json_parser +{ + int the_stack[JSON_PARSER_MAX_DEPTH]; + zval *the_zstack[JSON_PARSER_MAX_DEPTH]; + int the_top; +} json_parser; + + +/* + These modes can be pushed on the PDA stack. +*/ +#define MODE_DONE 1 +#define MODE_KEY 2 +#define MODE_OBJECT 3 +#define MODE_ARRAY 4 + +/* + Push a mode onto the stack. Return false if there is overflow. +*/ +static int +push(json_parser *json, zval *z, int mode) +{ + json->the_top += 1; + if (json->the_top >= JSON_PARSER_MAX_DEPTH) { + return false; + } + + json->the_stack[json->the_top] = mode; + return true; +} + + +/* + Pop the stack, assuring that the current mode matches the expectation. + Return false if there is underflow or if the modes mismatch. +*/ +static int +pop(json_parser *json, zval *z, int mode) +{ + if (json->the_top < 0 || json->the_stack[json->the_top] != mode) { + return false; + } + json->the_stack[json->the_top] = 0; + json->the_top -= 1; + + return true; +} + + +static int dehexchar(char c) +{ + if (c >= '0' && c <= '9') + { + return c - '0'; + } + else if (c >= 'A' && c <= 'F') + { + return c - ('A' - 10); + } + else if (c >= 'a' && c <= 'f') + { + return c - ('a' - 10); + } + else + { + return -1; + } +} + + +static void json_create_zval(zval **z, smart_str *buf, int type) +{ + ALLOC_INIT_ZVAL(*z); + + if (type == IS_LONG) + { + ZVAL_LONG(*z, atol(buf->c)); + } + else if (type == IS_DOUBLE) + { + ZVAL_DOUBLE(*z, atof(buf->c)); + } + else if (type == IS_STRING) + { + ZVAL_STRINGL(*z, buf->c, buf->len, 1); + } + else if (type == IS_BOOL) + { + ZVAL_BOOL(*z, (*(buf->c) == 't')); + } + else /* type == IS_NULL) || type unknown */ + { + ZVAL_NULL(*z); + } +} + + +static void utf16_to_utf8(smart_str *buf, unsigned short utf16) +{ + if (utf16 < 0x80) + { + smart_str_appendc(buf, (unsigned char) utf16); + } + else if (utf16 < 0x800) + { + smart_str_appendc(buf, 0xc0 | (utf16 >> 6)); + smart_str_appendc(buf, 0x80 | (utf16 & 0x3f)); + } + else + { + smart_str_appendc(buf, 0xe0 | (utf16 >> 12)); + smart_str_appendc(buf, 0x80 | ((utf16 >> 6) & 0x3f)); + smart_str_appendc(buf, 0x80 | (utf16 & 0x3f)); + } +} + +static void attach_zval(json_parser *json, int up, int cur, smart_str *key, int assoc TSRMLS_DC) +{ + zval *root = json->the_zstack[up]; + zval *child = json->the_zstack[cur]; + int up_mode = json->the_stack[up]; + + if (up_mode == MODE_ARRAY) + { + add_next_index_zval(root, child); + } + else if (up_mode == MODE_OBJECT) + { + if (!assoc) + { + add_property_zval(root, key->c, child); +#if PHP_MAJOR_VERSION >= 5 + ZVAL_DELREF(child); +#endif + } + else + { + add_assoc_zval(root, key->c, child); + } + key->len = 0; + } +} + + +#define FREE_BUFFERS() do { smart_str_free(&buf); smart_str_free(&key); } while (0); +#define SWAP_BUFFERS(from, to) do { \ + char *t1 = from.c; \ + int t2 = from.a; \ + from.c = to.c; \ + from.a = to.a; \ + to.c = t1; \ + to.a = t2; \ + to.len = from.len; \ + from.len = 0; \ + } while(0); +#define JSON_RESET_TYPE() do { type = -1; } while(0); +#define JSON(x) the_json.x + + +/* + The JSON_parser takes a UTF-16 encoded string and determines if it is a + syntactically correct JSON text. Along the way, it creates a PHP variable. + + It is implemented as a Pushdown Automaton; that means it is a finite state + machine with a stack. +*/ +int +JSON_parser(zval *z, unsigned short p[], int length, int assoc TSRMLS_DC) +{ + int b; /* the next character */ + int c; /* the next character class */ + int s; /* the next state */ + json_parser the_json; /* the parser state */ + int the_state = 0; + int the_index; + + smart_str buf = {0}; + smart_str key = {0}; + + int type = -1; + unsigned short utf16; + + JSON(the_top) = -1; + push(&the_json, z, MODE_DONE); + + for (the_index = 0; the_index < length; the_index += 1) { + b = p[the_index]; + if ((b & 127) == b) { + c = ascii_class[b]; + if (c <= S_ERR) { + FREE_BUFFERS(); + return false; + } + } else { + c = S_ETC; + } +/* + Get the next state from the transition table. +*/ + s = state_transition_table[the_state][c]; + if (s < 0) { +/* + Perform one of the predefined actions. +*/ + switch (s) { +/* + empty } +*/ + case -9: + if (!pop(&the_json, z, MODE_KEY)) { + FREE_BUFFERS(); + return false; + } + the_state = 9; + break; +/* + { +*/ + case -8: + if (!push(&the_json, z, MODE_KEY)) { + FREE_BUFFERS(); + return false; + } + + the_state = 1; + if (JSON(the_top) > 0) + { + zval *obj; + + if (JSON(the_top) == 1) + { + obj = z; + } + else + { + ALLOC_INIT_ZVAL(obj); + } + + if (!assoc) + { + object_init(obj); + } + else + { + array_init(obj); + } + + JSON(the_zstack)[JSON(the_top)] = obj; + + if (JSON(the_top) > 1) + { + attach_zval(&the_json, JSON(the_top-1), JSON(the_top), &key, assoc TSRMLS_CC); + } + + JSON_RESET_TYPE(); + } + + break; +/* + } +*/ + case -7: + if (type != -1 && + (JSON(the_stack)[JSON(the_top)] == MODE_OBJECT || + JSON(the_stack)[JSON(the_top)] == MODE_ARRAY)) + { + zval *mval; + smart_str_0(&buf); + + json_create_zval(&mval, &buf, type); + + if (!assoc) + { + add_property_zval(JSON(the_zstack)[JSON(the_top)], key.c, mval); +#if PHP_MAJOR_VERSION >= 5 + ZVAL_DELREF(mval); +#endif + } + else + { + add_assoc_zval(JSON(the_zstack)[JSON(the_top)], key.c, mval); + } + key.len = 0; + buf.len = 0; + JSON_RESET_TYPE(); + } + + + if (!pop(&the_json, z, MODE_OBJECT)) { + FREE_BUFFERS(); + return false; + } + the_state = 9; + break; +/* + [ +*/ + case -6: + if (!push(&the_json, z, MODE_ARRAY)) { + FREE_BUFFERS(); + return false; + } + the_state = 2; + + if (JSON(the_top) > 0) + { + zval *arr; + + if (JSON(the_top) == 1) + { + arr = z; + } + else + { + ALLOC_INIT_ZVAL(arr); + } + + array_init(arr); + JSON(the_zstack)[JSON(the_top)] = arr; + + if (JSON(the_top) > 1) + { + attach_zval(&the_json, JSON(the_top-1), JSON(the_top), &key, assoc TSRMLS_CC); + } + + JSON_RESET_TYPE(); + } + + break; +/* + ] +*/ + case -5: + { + if (type != -1 && + (JSON(the_stack)[JSON(the_top)] == MODE_OBJECT || + JSON(the_stack)[JSON(the_top)] == MODE_ARRAY)) + { + zval *mval; + smart_str_0(&buf); + + json_create_zval(&mval, &buf, type); + add_next_index_zval(JSON(the_zstack)[JSON(the_top)], mval); + buf.len = 0; + JSON_RESET_TYPE(); + } + + if (!pop(&the_json, z, MODE_ARRAY)) { + FREE_BUFFERS(); + return false; + } + the_state = 9; + } + break; +/* + " +*/ + case -4: + switch (JSON(the_stack)[JSON(the_top)]) { + case MODE_KEY: + the_state = 27; + smart_str_0(&buf); + SWAP_BUFFERS(buf, key); + JSON_RESET_TYPE(); + break; + case MODE_ARRAY: + case MODE_OBJECT: + the_state = 9; + break; + default: + FREE_BUFFERS(); + return false; + } + break; +/* + , +*/ + case -3: + { + zval *mval; + + if (type != -1 && + (JSON(the_stack)[JSON(the_top)] == MODE_OBJECT || + JSON(the_stack[JSON(the_top)]) == MODE_ARRAY)) + { + smart_str_0(&buf); + json_create_zval(&mval, &buf, type); + } + + switch (JSON(the_stack)[JSON(the_top)]) { + case MODE_OBJECT: + if (pop(&the_json, z, MODE_OBJECT) && push(&the_json, z, MODE_KEY)) { + if (type != -1) + { + if (!assoc) + { + add_property_zval(JSON(the_zstack)[JSON(the_top)], (key.len ? key.c : "_empty_"), mval); +#if PHP_MAJOR_VERSION >= 5 + ZVAL_DELREF(mval); +#endif + } + else + { + add_assoc_zval(JSON(the_zstack)[JSON(the_top)], (key.len ? key.c : "_empty_"), mval); + } + key.len = 0; + } + the_state = 29; + } + break; + case MODE_ARRAY: + if (type != -1) + { + add_next_index_zval(JSON(the_zstack)[JSON(the_top)], mval); + } + the_state = 28; + break; + default: + FREE_BUFFERS(); + return false; + } + buf.len = 0; + JSON_RESET_TYPE(); + } + break; +/* + : +*/ + case -2: + if (pop(&the_json, z, MODE_KEY) && push(&the_json, z, MODE_OBJECT)) { + the_state = 28; + break; + } +/* + syntax error +*/ + case -1: + { + FREE_BUFFERS(); + return false; + } + } + } else { +/* + Change the state and iterate. +*/ + if (type == IS_STRING) + { + if (s == 3 && the_state != 8) + { + if (the_state != 4) + { + utf16_to_utf8(&buf, b); + } + else + { + switch (b) + { + case 'b': + smart_str_appendc(&buf, '\b'); + break; + case 't': + smart_str_appendc(&buf, '\t'); + break; + case 'n': + smart_str_appendc(&buf, '\n'); + break; + case 'f': + smart_str_appendc(&buf, '\f'); + break; + case 'r': + smart_str_appendc(&buf, '\r'); + break; + default: + utf16_to_utf8(&buf, b); + break; + } + } + } + else if (s == 6) + { + utf16 = dehexchar(b) << 12; + } + else if (s == 7) + { + utf16 += dehexchar(b) << 8; + } + else if (s == 8) + { + utf16 += dehexchar(b) << 4; + } + else if (s == 3 && the_state == 8) + { + utf16 += dehexchar(b); + utf16_to_utf8(&buf, utf16); + } + } + else if (type < IS_LONG && (c == S_DIG || c == S_ZER)) + { + type = IS_LONG; + smart_str_appendc(&buf, b); + } + else if (type == IS_LONG && s == 24) + { + type = IS_DOUBLE; + smart_str_appendc(&buf, b); + } + else if (type < IS_DOUBLE && c == S_DOT) + { + type = IS_DOUBLE; + smart_str_appendc(&buf, b); + } + else if (type < IS_STRING && c == S_QUO) + { + type = IS_STRING; + } + else if (type < IS_BOOL && ((the_state == 12 && s == 9) || (the_state == 16 && s == 9))) + { + type = IS_BOOL; + } + else if (type < IS_NULL && the_state == 19 && s == 9) + { + type = IS_NULL; + } + else if (type != IS_STRING && c > S_WSP) + { + utf16_to_utf8(&buf, b); + } + + the_state = s; + } + } + + FREE_BUFFERS(); + + return the_state == 9 && pop(&the_json, z, MODE_DONE); +} + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/json/JSON_parser.h b/ext/json/JSON_parser.h new file mode 100644 index 0000000000..085e776982 --- /dev/null +++ b/ext/json/JSON_parser.h @@ -0,0 +1,8 @@ +/* JSON_checker.h */ + +#include "php.h" +#include "ext/standard/php_smart_str.h" + +static char digits[] = "0123456789abcdef"; + +extern int JSON_parser(zval *z, unsigned short p[], int length, int assoc TSRMLS_DC); diff --git a/ext/json/README b/ext/json/README new file mode 100644 index 0000000000..d680b0c592 --- /dev/null +++ b/ext/json/README @@ -0,0 +1,76 @@ +json 1.2.0 +========== + +This extension implements the JavaScript Object Notation (JSON) +data-interchange format as specified in [0]. + +Two functions are implemented: encoding and decoding. The decoding +is handled by a parser based on JSON_checker[1] by Douglas Crockford. + + +Function overview +----------------- + + string json_encode ( mixed value ) + +json_encode returns a string containing the JSON representation of value. +value can be any type except a resource. + + mixed json_decode ( string json, [bool assoc] ) + +json_decode takes a JSON string and converts it into a PHP variable. +When assoc is given, and evaluates to TRUE, json_decode() will return +any objects as associative arrays. + + +Example usage +------------- + +$arr = array("a"=>1,"b"=>2,"c"=>3,"d"=>4,"e"=>5); +echo json_encode($arr); + +---> {"a":1,"b":2,"c":3,"d":4,"e":5} + +$json = '{"a":1,"b":2,"c":3,"d":4,"e":5}'; +var_dump(json_decode($json)); + +---> object(stdClass)#1 (5) { + ["a"]=> + int(1) + ["b"]=> + int(2) + ["c"]=> + int(3) + ["d"]=> + int(4) + ["e"]=> + int(5) + } + +$json = '{"a":1,"b":2,"c":3,"d":4,"e":5}'; +var_dump(json_decode($json, true)); + +---> array(5) { + ["a"]=> + int(1) + ["b"]=> + int(2) + ["c"]=> + int(3) + ["d"]=> + int(4) + ["e"]=> + int(5) + } + + +Authors +------- + +Omar Kilani <omar@php.net> + + +--- + +[0] http://www.crockford.com/JSON/draft-jsonorg-json-00.txt +[1] http://www.crockford.com/JSON/JSON_checker/ diff --git a/ext/json/config.m4 b/ext/json/config.m4 new file mode 100644 index 0000000000..a937b1f91b --- /dev/null +++ b/ext/json/config.m4 @@ -0,0 +1,88 @@ +dnl +dnl $Id$ +dnl + +AC_DEFUN([PHP_JSON_ADD_SOURCES], [ + PHP_JSON_SOURCES="$PHP_JSON_SOURCES $1" +]) + +AC_DEFUN([PHP_JSON_ADD_BASE_SOURCES], [ + PHP_JSON_BASE_SOURCES="$PHP_JSON_BASE_SOURCES $1" +]) + +AC_DEFUN([PHP_JSON_ADD_BUILD_DIR], [ + PHP_JSON_EXTRA_BUILD_DIRS="$PHP_JSON_EXTRA_BUILD_DIRS $1" +]) + +AC_DEFUN([PHP_JSON_ADD_INCLUDE], [ + PHP_JSON_EXTRA_INCLUDES="$PHP_JSON_EXTRA_INCLUDES $1" +]) + +AC_DEFUN([PHP_JSON_ADD_CONFIG_HEADER], [ + PHP_JSON_EXTRA_CONFIG_HEADERS="$PHP_JSON_EXTRA_CONFIG_HEADERS $1" +]) + +AC_DEFUN([PHP_JSON_ADD_CFLAG], [ + PHP_JSON_CFLAGS="$PHP_JSON_CFLAGS $1" +]) + +AC_DEFUN([PHP_JSON_EXTENSION], [ + PHP_NEW_EXTENSION(json, $PHP_JSON_SOURCES, $ext_shared,, $PHP_JSON_CFLAGS) + PHP_SUBST(JSON_SHARED_LIBADD) + + for dir in $PHP_JSON_EXTRA_BUILD_DIRS; do + PHP_ADD_BUILD_DIR([$ext_builddir/$dir], 1) + done + + for dir in $PHP_JSON_EXTRA_INCLUDES; do + PHP_ADD_INCLUDE([$ext_srcdir/$dir]) + PHP_ADD_INCLUDE([$ext_builddir/$dir]) + done + + if test "$ext_shared" = "no"; then + PHP_ADD_SOURCES(PHP_EXT_DIR(json), $PHP_JSON_BASE_SOURCES,$PHP_JSON_CFLAGS) + out="php_config.h" + else + PHP_ADD_SOURCES_X(PHP_EXT_DIR(json),$PHP_JSON_BASE_SOURCES,$PHP_JSON_CFLAGS,shared_objects_json,yes) + if test -f "$ext_builddir/config.h.in"; then + out="$abs_builddir/config.h" + else + out="php_config.h" + fi + fi + + for cfg in $PHP_JSON_EXTRA_CONFIG_HEADERS; do + cat > $ext_builddir/$cfg <<EOF +#include "$out" +EOF + done +]) + +AC_DEFUN([PHP_JSON_SETUP_JSON_CHECKER], [ + PHP_JSON_ADD_SOURCES([ + utf8_to_utf16.c + utf8_decode.c + JSON_parser.c + ]) +]) + +dnl +dnl Main config +dnl + +PHP_ARG_WITH(json, whether to enable JavaScript Object Serialization support, +[ --with-json Enable JavaScript Object Serialization support]) + +if test "$PHP_JSON" != "no"; then + AC_DEFINE([HAVE_JSON],1,[whether to have JavaScript Object Serialization support]) + AC_HEADER_STDC + + PHP_JSON_ADD_BASE_SOURCES([json.c]) + + dnl json_c is required + PHP_JSON_SETUP_JSON_CHECKER + PHP_JSON_EXTENSION + dnl PHP_INSTALL_HEADERS([ext/json], [json_c]) +fi + +# vim600: sts=2 sw=2 et diff --git a/ext/json/config.w32 b/ext/json/config.w32 new file mode 100644 index 0000000000..fa001d308c --- /dev/null +++ b/ext/json/config.w32 @@ -0,0 +1,10 @@ +// $Id$ +// vim:ft=javascript + +ARG_WITH("json", "JavaScript Object Serialization support", "no"); + +if (PHP_JSON != "no") { + EXTENSION('json', 'json.c', PHP_JSON_SHARED, ""); + ADD_SOURCES(configure_module_dirname, "JSON_parser.c utf8_decode.c utf8_to_utf16.c", "json"); +} + diff --git a/ext/json/json.c b/ext/json/json.c new file mode 100644 index 0000000000..d19eb2e9b5 --- /dev/null +++ b/ext/json/json.c @@ -0,0 +1,449 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Omar Kilani <omar@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "ext/standard/php_smart_str.h" +#include "utf8_to_utf16.h" +#include "JSON_parser.h" +#include "php_json.h" + +/* If you declare any globals in php_json.h uncomment this: +ZEND_DECLARE_MODULE_GLOBALS(json) +*/ + +/* True global resources - no need for thread safety here */ +static int le_json; + +/* {{{ json_functions[] + * + * Every user visible function must have an entry in json_functions[]. + */ +function_entry json_functions[] = { + PHP_FE(json_encode, NULL) + PHP_FE(json_decode, NULL) + {NULL, NULL, NULL} /* Must be the last line in json_functions[] */ +}; +/* }}} */ + +/* {{{ json_module_entry + */ +zend_module_entry json_module_entry = { +#if ZEND_MODULE_API_NO >= 20010901 + STANDARD_MODULE_HEADER, +#endif + "json", + json_functions, + NULL, + NULL, + NULL, + NULL, + PHP_MINFO(json), +#if ZEND_MODULE_API_NO >= 20010901 + PHP_JSON_VERSION, +#endif + STANDARD_MODULE_PROPERTIES +}; +/* }}} */ + +#ifdef COMPILE_DL_JSON +ZEND_GET_MODULE(json) +#endif + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(json) +{ + php_info_print_table_start(); + php_info_print_table_row(2, "json support", "enabled"); + php_info_print_table_row(2, "json version", PHP_JSON_VERSION); + php_info_print_table_end(); +} +/* }}} */ + +static void json_encode_r(smart_str *buf, zval *val TSRMLS_DC); +static void json_escape_string(smart_str *buf, char *s, int len TSRMLS_DC); + +static int json_determine_array_type(zval **val TSRMLS_DC) { + int i; + HashTable *myht; + + if (Z_TYPE_PP(val) == IS_ARRAY) { + myht = HASH_OF(*val); + } else { + myht = Z_OBJPROP_PP(val); + return 1; + } + + i = myht ? zend_hash_num_elements(myht) : 0; + if (i > 0) { + char *key; + ulong index, idx; + uint key_len; + HashPosition pos; + + zend_hash_internal_pointer_reset_ex(myht, &pos); + idx = 0; + for (;; zend_hash_move_forward_ex(myht, &pos)) { + i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos); + if (i == HASH_KEY_NON_EXISTANT) + break; + + if (i == HASH_KEY_IS_STRING) { + return 1; + } else { + if (index != idx) { + return 1; + } + } + idx++; + } + } + + return 0; +} + +static void json_encode_array(smart_str *buf, zval **val TSRMLS_DC) { + int i, r; + HashTable *myht; + + if (Z_TYPE_PP(val) == IS_ARRAY) { + myht = HASH_OF(*val); + r = json_determine_array_type(val TSRMLS_CC); + } else { + myht = Z_OBJPROP_PP(val); + r = 1; + } + + if (r == 0) + { + smart_str_appendc(buf, '['); + } + else + { + smart_str_appendc(buf, '{'); + } + + i = myht ? zend_hash_num_elements(myht) : 0; + if (i > 0) { + char *key; + zval **data; + ulong index; + uint key_len; + HashPosition pos; + int need_comma = 0; + + zend_hash_internal_pointer_reset_ex(myht, &pos); + for (;; zend_hash_move_forward_ex(myht, &pos)) { + i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos); + if (i == HASH_KEY_NON_EXISTANT) + break; + + if (zend_hash_get_current_data_ex(myht, (void **) &data, &pos) == SUCCESS) { + if (r == 0) { + if (need_comma) { + smart_str_appendc(buf, ','); + } else { + need_comma = 1; + } + + json_encode_r(buf, *data TSRMLS_CC); + } else if (r == 1) { + if (i == HASH_KEY_IS_STRING) { + if (key[0] == '\0') { + /* Skip protected and private members. */ + continue; + } + + if (need_comma) { + smart_str_appendc(buf, ','); + } else { + need_comma = 1; + } + + json_escape_string(buf, key, key_len - 1 TSRMLS_CC); + smart_str_appendc(buf, ':'); + + json_encode_r(buf, *data TSRMLS_CC); + } else { + if (need_comma) { + smart_str_appendc(buf, ','); + } else { + need_comma = 1; + } + + smart_str_appendc(buf, '"'); + smart_str_append_long(buf, (long) index); + smart_str_appendc(buf, '"'); + smart_str_appendc(buf, ':'); + + json_encode_r(buf, *data TSRMLS_CC); + } + } + } + } + } + + if (r == 0) + { + smart_str_appendc(buf, ']'); + } + else + { + smart_str_appendc(buf, '}'); + } +} + +#define REVERSE16(us) (((us & 0xf) << 12) | (((us >> 4) & 0xf) << 8) | (((us >> 8) & 0xf) << 4) | ((us >> 12) & 0xf)) + +static void json_escape_string(smart_str *buf, char *s, int len TSRMLS_DC) +{ + int pos = 0; + unsigned short us; + unsigned short *utf16; + + if (len == 0) + { + smart_str_appendl(buf, "\"\"", 2); + return; + } + + utf16 = (unsigned short *) emalloc(len * sizeof(unsigned short)); + + len = utf8_to_utf16(utf16, s, len); + if (len <= 0) + { + if (utf16) + { + efree(utf16); + } + + smart_str_appendl(buf, "\"\"", 2); + return; + } + + smart_str_appendc(buf, '"'); + + while(pos < len) + { + us = utf16[pos++]; + + switch (us) + { + case '"': + { + smart_str_appendl(buf, "\\\"", 2); + } + break; + case '\\': + { + smart_str_appendl(buf, "\\\\", 2); + } + break; + case '/': + { + smart_str_appendl(buf, "\\/", 2); + } + break; + case '\b': + { + smart_str_appendl(buf, "\\b", 2); + } + break; + case '\f': + { + smart_str_appendl(buf, "\\f", 2); + } + break; + case '\n': + { + smart_str_appendl(buf, "\\n", 2); + } + break; + case '\r': + { + smart_str_appendl(buf, "\\r", 2); + } + break; + case '\t': + { + smart_str_appendl(buf, "\\t", 2); + } + break; + default: + { + if (us < ' ' || (us & 127) == us) + { + smart_str_appendc(buf, (unsigned char) us); + } + else + { + smart_str_appendl(buf, "\\u", 2); + us = REVERSE16(us); + + smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]); + us >>= 4; + smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]); + us >>= 4; + smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]); + us >>= 4; + smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]); + } + } + break; + } + } + + smart_str_appendc(buf, '"'); + efree(utf16); +} + +static void json_encode_r(smart_str *buf, zval *val TSRMLS_DC) { + switch (Z_TYPE_P(val)) { + case IS_NULL: + smart_str_appendl(buf, "null", 4); + break; + case IS_BOOL: + if (Z_BVAL_P(val)) + { + smart_str_appendl(buf, "true", 4); + } + else + { + smart_str_appendl(buf, "false", 5); + } + break; + case IS_LONG: + smart_str_append_long(buf, Z_LVAL_P(val)); + break; + case IS_DOUBLE: + { + char *d = NULL; + int len; + double dbl = Z_DVAL_P(val); + + if (!zend_isinf(dbl) && !zend_isnan(dbl)) + { + len = spprintf(&d, 0, "%.9g", dbl); + if (d) + { + smart_str_appendl(buf, d, len); + efree(d); + } + } + else + { + zend_error(E_WARNING, "[json] (json_encode_r) double %.9g does not conform to the JSON spec, encoded as 0.", dbl); + smart_str_appendc(buf, '0'); + } + } + break; + case IS_STRING: + json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val) TSRMLS_CC); + break; + case IS_ARRAY: + case IS_OBJECT: + json_encode_array(buf, &val TSRMLS_CC); + break; + default: + zend_error(E_WARNING, "[json] (json_encode_r) type is unsupported, encoded as null."); + smart_str_appendl(buf, "null", 4); + break; + } + + return; +} + +PHP_FUNCTION(json_encode) +{ + zval *parameter; + smart_str buf = {0}; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", ¶meter) == FAILURE) { + return; + } + + json_encode_r(&buf, parameter TSRMLS_CC); + + ZVAL_STRINGL(return_value, buf.c, buf.len, 1); + + smart_str_free(&buf); +} + +PHP_FUNCTION(json_decode) +{ + char *parameter; + int parameter_len, utf16_len; + zend_bool assoc = 0; /* return JS objects as PHP objects by default */ + zval *z; + unsigned short *utf16; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", ¶meter, ¶meter_len, &assoc) == FAILURE) { + return; + } + + if (!parameter_len) + { + RETURN_NULL(); + } + + utf16 = (unsigned short *) emalloc((parameter_len+1) * sizeof(unsigned short)); + + utf16_len = utf8_to_utf16(utf16, parameter, parameter_len); + if (utf16_len <= 0) + { + if (utf16) + { + efree(utf16); + } + + RETURN_NULL(); + } + + ALLOC_INIT_ZVAL(z); + if (JSON_parser(z, utf16, utf16_len, assoc TSRMLS_CC)) + { + *return_value = *z; + + FREE_ZVAL(z); + efree(utf16); + } + else + { + zval_dtor(z); + FREE_ZVAL(z); + efree(utf16); + RETURN_NULL(); + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/json/json.dsp b/ext/json/json.dsp new file mode 100644 index 0000000000..e5bb3767bf --- /dev/null +++ b/ext/json/json.dsp @@ -0,0 +1,135 @@ +# Microsoft Developer Studio Project File - Name="json" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=json - Win32 Debug_TS
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "json.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "json.mak" CFG="json - Win32 Debug_TS"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "json - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "json - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "json - Win32 Debug_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug_TS"
+# PROP BASE Intermediate_Dir "Debug_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug_TS"
+# PROP Intermediate_Dir "Debug_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JSON_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\.." /I "..\..\main" /I "..\..\Zend" /I "..\..\TSRM" /I "json_c" /D HAVE_JSON=1 /D "ZEND_WIN32" /D "PHP_WIN32" /D ZEND_DEBUG=1 /D ZTS=1 /D COMPILE_DL_JSON=1 /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JSON_EXPORTS" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x1009 /d "_DEBUG"
+# ADD RSC /l 0x1009 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 iconv.lib php4ts_debug.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"..\..\Debug_TS/php_json.dll" /pdbtype:sept /libpath:"..\..\Debug_TS"
+
+!ELSEIF "$(CFG)" == "json - Win32 Release_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release_TS"
+# PROP BASE Intermediate_Dir "Release_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release_TS"
+# PROP Intermediate_Dir "Release_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JSON_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\.." /I "..\..\main" /I "..\..\Zend" /I "..\..\TSRM" /I "json_c" /D HAVE_JSON=1 /D "ZEND_WIN32" /D ZEND_DEBUG=0 /D "PHP_WIN32" /D ZTS=1 /D COMPILE_DL_JSON=1 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JSON_EXPORTS" /D "HAVE_FCNTL_H" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x1009 /d "NDEBUG"
+# ADD RSC /l 0x1009 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 iconv.lib php4ts.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"..\..\Release_TS/php_json.dll" /libpath:"..\..\Release_TS"
+
+!ENDIF
+
+# Begin Target
+
+# Name "json - Win32 Debug_TS"
+# Name "json - Win32 Release_TS"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=".\json.c"
+# End Source File
+# Begin Source File
+
+SOURCE=.\JSON_parser.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\JSON_parser.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\utf8_decode.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\utf8_decode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\utf8_to_utf16.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\utf8_to_utf16.h
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\php_json.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/ext/json/package.xml b/ext/json/package.xml new file mode 100644 index 0000000000..0651de736f --- /dev/null +++ b/ext/json/package.xml @@ -0,0 +1,152 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> +<!DOCTYPE package SYSTEM "../pear/package.dtd"> +<package> + <dep type="php" rel="ge" version="4.3.0" optional="no"/> + <name>json</name> + <summary>JavaScript Object Notation</summary> + <maintainers> + <maintainer> + <user>omar</user> + <name>Omar Kilani</name> + <email>omar@php.net</email> + <role>lead</role> + </maintainer> + </maintainers> + <description> + Support for JSON (JavaScript Object Notation) serialization. + </description> + <license>PHP 3.01</license> + <release> + <state>stable</state> + <version>1.2.1</version> + <date>2006-03-18</date> + <notes> + Fix PECL bug #7147 - rework handling of comma insertion while encoding. + Add tests to package.xml + </notes> + </release> + <configureoptions> + </configureoptions> + <filelist> + <file role="doc" name="README" /> + <file role="src" name="config.m4" /> + <file role="src" name="config.w32" /> + <file role="src" name="json.dsp" /> + <file role="src" name="json.c" /> + <file role="src" name="JSON_parser.c" /> + <file role="src" name="JSON_parser.h" /> + <file role="src" name="php_json.h" /> + <file role="src" name="utf8_decode.c" /> + <file role="src" name="utf8_decode.h" /> + <file role="src" name="utf8_to_utf16.c" /> + <file role="src" name="utf8_to_utf16.h" /> + <dir role="test" name="tests"> + <file role="test" name="fail001.phpt" /> + <file role="test" name="pass001.phpt" /> + <file role="test" name="pass001.1.phpt" /> + <file role="test" name="pass002.phpt" /> + <file role="test" name="pass003.phpt" /> + </dir> + </filelist> + <changelog> + <release> + <state>stable</state> + <version>1.0.0</version> + <date>2005-04-01</date> + <notes> + Initial release. + </notes> + </release> + <release> + <state>stable</state> + <version>1.0.1</version> + <date>2005-06-10</date> + <notes> + Fixed non-linear and mixed type array index issues, fixed issues with escaping \\, forked json-c and added Unicode support. + </notes> + </release> + <release> + <state>stable</state> + <version>1.0.2</version> + <date>2005-06-11</date> + <notes> + Fixed issues with object reference counts under PHP4. + </notes> + </release> + <release> + <state>stable</state> + <version>1.0.3</version> + <date>2005-06-15</date> + <notes> + Fixed json-c string corruption issues under Mac OS X and FreeBSD. + </notes> + </release> + <release> + <state>stable</state> + <version>1.0.4</version> + <date>2005-06-15</date> + <notes> + Changes in 1.0.4 released with 1.0.5. + </notes> + </release> + <release> + <state>stable</state> + <version>1.0.5</version> + <date>2005-06-16</date> + <notes> + Changed spacing in json-c encoding, added optional assoc (boolean) parameter to json_decode to decode as associative array instead of object, fixed issues with escaping /. + </notes> + </release> + <release> + <state>stable</state> + <version>1.0.6</version> + <date>2005-08-05</date> + <notes> + Fixed issues with exporting private and protected class members. + </notes> + </release> + <release> + <state>stable</state> + <version>1.0.7</version> + <date>2005-09-07</date> + <notes> + Fixed issues with negative array keys, modified json-c to return an error on unquoted object key names instead of going into an infinite loop. + </notes> + </release> + <release> + <state>stable</state> + <version>1.0.8</version> + <date>2005-12-01</date> + <notes> + Changed license to LGPL, modified build system to allow static compilation into PHP, added strndup check for json-c. + </notes> + </release> + <release> + <state>stable</state> + <version>1.1.0</version> + <date>2005-12-04</date> + <notes> + Port to Win32. + </notes> + </release> + <release> + <state>stable</state> + <version>1.1.1</version> + <date>2006-01-12</date> + <notes> + Cleanup and TSRM performance fixes by rasmus. + </notes> + </release> + <release> + <state>stable</state> + <version>1.2.0</version> + <date>2006-03-15</date> + <notes> + Complete rewrite using JSON_checker as the base for the parser. Implements the JSON specification. 3-8x faster on encodes and 1.2x-4x faster on decodes. + </notes> + </release> + </changelog> +</package> +<!-- +vim:et:ts=1:sw=1 +--> diff --git a/ext/json/php_json.h b/ext/json/php_json.h new file mode 100644 index 0000000000..48555ee31f --- /dev/null +++ b/ext/json/php_json.h @@ -0,0 +1,60 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Omar Kilani <omar@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_JSON_H +#define PHP_JSON_H + +#define PHP_JSON_VERSION "1.2.1" + +extern zend_module_entry json_module_entry; +#define phpext_json_ptr &json_module_entry + +#ifdef PHP_WIN32 +#define PHP_JSON_API __declspec(dllexport) +#else +#define PHP_JSON_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +PHP_MINFO_FUNCTION(json); + +PHP_FUNCTION(json_encode); +PHP_FUNCTION(json_decode); + +#ifdef ZTS +#define JSON_G(v) TSRMG(json_globals_id, zend_json_globals *, v) +#else +#define JSON_G(v) (json_globals.v) +#endif + +#endif /* PHP_JSON_H */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/json/tests/fail001.phpt b/ext/json/tests/fail001.phpt new file mode 100644 index 0000000000..4ee3780cf3 --- /dev/null +++ b/ext/json/tests/fail001.phpt @@ -0,0 +1,166 @@ +--TEST-- +JSON Test Pattern fail1 -> fail24 +http://www.crockford.com/JSON/JSON_checker/test/fail*.json +--SKIPIF-- +<?php + if (!extension_loaded('json')) die('skip: json extension not available'); +?> +--FILE-- +<?php + +$tests = array('"A JSON payload should be an object or array, not a string."', + '["Unclosed array"', + '{unquoted_key: "keys must be quoted}', + '["extra comma",]', + '["double extra comma",,]', + '[ , "<-- missing value"]', + '["Comma after the close"],', + '["Extra close"]]', + '{"Extra comma": true,}', + '{"Extra value after close": true} "misplaced quoted value"', + '{"Illegal expression": 1 + 2}', + '{"Illegal invocation": alert()}', + '{"Numbers cannot have leading zeroes": 013}', + '{"Numbers cannot be hex": 0x14}', + '["Illegal backslash escape: \\x15"]', + '["Illegal backslash escape: \\\'"]', + '["Illegal backslash escape: \\017"]', + '[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]', + '{"Missing colon" null}', + '{"Double colon":: null}', + '{"Comma instead of colon", null}', + '["Colon instead of comma": false]', + '["Bad value", truth]', + "['single quote']"); + +foreach ($tests as $test) +{ + echo 'Testing: ' . $test . "\n"; + echo "AS OBJECT\n"; + var_dump(json_decode($test)); + echo "AS ARRAY\n"; + var_dump(json_decode($test, true)); +} + +?> +--EXPECT-- +Testing: "A JSON payload should be an object or array, not a string." +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ["Unclosed array" +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {unquoted_key: "keys must be quoted} +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ["extra comma",] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ["double extra comma",,] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: [ , "<-- missing value"] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ["Comma after the close"], +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ["Extra close"]] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {"Extra comma": true,} +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {"Extra value after close": true} "misplaced quoted value" +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {"Illegal expression": 1 + 2} +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {"Illegal invocation": alert()} +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {"Numbers cannot have leading zeroes": 013} +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {"Numbers cannot be hex": 0x14} +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ["Illegal backslash escape: \x15"] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ["Illegal backslash escape: \'"] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ["Illegal backslash escape: \017"] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: [[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {"Missing colon" null} +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {"Double colon":: null} +AS OBJECT +NULL +AS ARRAY +NULL +Testing: {"Comma instead of colon", null} +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ["Colon instead of comma": false] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ["Bad value", truth] +AS OBJECT +NULL +AS ARRAY +NULL +Testing: ['single quote'] +AS OBJECT +NULL +AS ARRAY +NULL diff --git a/ext/json/tests/pass001.1.phpt b/ext/json/tests/pass001.1.phpt new file mode 100644 index 0000000000..0cb05308da --- /dev/null +++ b/ext/json/tests/pass001.1.phpt @@ -0,0 +1,895 @@ +--TEST-- +JSON Test Pattern pass1.1 +Modified to test unescaped UNICODE as keys and values. +Modified to test numbers with exponents without a decimal point. +Modified to test empty string values. +Modified to test a mix of integers and strings as keys. +http://www.crockford.com/JSON/JSON_checker/test/pass1.json +--SKIPIF-- +<?php + if (!extension_loaded('json')) die('skip: json extension not available'); +?> +--FILE-- +<?php +// Expect warnings about INF. +ini_set("error_reporting", E_ALL & ~E_WARNING); + +$test = " +[ + \"JSON Test Pattern pass1\", + {\"object with 1 member\":[\"array with 1 element\"]}, + {}, + [], + -42, + true, + false, + null, + { + \"integer\": 1234567890, + \"real\": -9876.543210, + \"e\": 0.123456789e-12, + \"E\": 1.234567890E+34, + \"\": 23456789012E666, + \"E no .\": 4E12, + \"zero\": 0, + \"one\": 1, + \"space\": \" \", + \"quote\": \"\\\"\", + \"backslash\": \"\\\\\", + \"controls\": \"\\b\\f\\n\\r\\t\", + \"slash\": \"/ & \\/\", + \"alpha\": \"abcdefghijklmnopqrstuvwyz\", + \"ALPHA\": \"ABCDEFGHIJKLMNOPQRSTUVWYZ\", + \"digit\": \"0123456789\", + \"special\": \"`1~!@#$%^&*()_+-={':[,]}|;.</>?\", + \"hex\": \"\\u0123\\u4567\\u89AB\\uCDEF\\uabcd\\uef4A\", + \"unicode\": \"\\u30d7\\u30ec\\u30b9\\u30ad\\u30c3\\u30c8\", + \"プレスキット\": \"プレスキット\", + \"empty_string\": \"\", + \"true\": true, + \"false\": false, + \"null\": null, + \"array\":[ ], + \"object\":{ }, + \"123\":{\"456\":{\"abc\":{\"789\":\"def\",\"012\":[1,2,\"5\",500],\"ghi\":[1,2,\"five\",50,\"sixty\"]}}}, + \"address\": \"50 St. James Street\", + \"url\": \"http://www.JSON.org/\", + \"comment\": \"// /* <!-- --\", + \"# -- --> */\": \" \", + \" s p a c e d \" :[1,2 , 3 + +, + +4 , 5 , 6 ,7 ], + \"compact\": [1,2,3,4,5,6,7], + \"jsontext\": \"{\\\"object with 1 member\\\":[\\\"array with 1 element\\\"]}\", + \"quotes\": \"" \\u0022 %22 0x22 034 "\", + \"\\/\\\\\\\"\\uCAFE\\uBABE\\uAB98\\uFCDE\\ubcda\\uef4A\\b\\f\\n\\r\\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?\" +: \"A key can be any string\" + }, + 0.5 ,98.6 +, +99.44 +, + +1066 + + +,\"rosebud\"] +"; + +echo 'Testing: ' . $test . "\n"; +echo "DECODE: AS OBJECT\n"; +$obj = json_decode($test); +var_dump($obj); +echo "DECODE: AS ARRAY\n"; +$arr = json_decode($test, true); +var_dump($arr); + +echo "ENCODE: FROM OBJECT\n"; +$obj_enc = json_encode($obj); +echo $obj_enc . "\n"; +echo "ENCODE: FROM ARRAY\n"; +$arr_enc = json_encode($arr); +echo $arr_enc . "\n"; + +echo "DECODE AGAIN: AS OBJECT\n"; +$obj = json_decode($obj_enc); +var_dump($obj); +echo "DECODE AGAIN: AS ARRAY\n"; +$arr = json_decode($arr_enc, true); +var_dump($arr); + +?> +--EXPECT-- +Testing: +[ + "JSON Test Pattern pass1", + {"object with 1 member":["array with 1 element"]}, + {}, + [], + -42, + true, + false, + null, + { + "integer": 1234567890, + "real": -9876.543210, + "e": 0.123456789e-12, + "E": 1.234567890E+34, + "": 23456789012E666, + "E no .": 4E12, + "zero": 0, + "one": 1, + "space": " ", + "quote": "\"", + "backslash": "\\", + "controls": "\b\f\n\r\t", + "slash": "/ & \/", + "alpha": "abcdefghijklmnopqrstuvwyz", + "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ", + "digit": "0123456789", + "special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?", + "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A", + "unicode": "\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8", + "プレスキット": "プレスキット", + "empty_string": "", + "true": true, + "false": false, + "null": null, + "array":[ ], + "object":{ }, + "123":{"456":{"abc":{"789":"def","012":[1,2,"5",500],"ghi":[1,2,"five",50,"sixty"]}}}, + "address": "50 St. James Street", + "url": "http://www.JSON.org/", + "comment": "// /* <!-- --", + "# -- --> */": " ", + " s p a c e d " :[1,2 , 3 + +, + +4 , 5 , 6 ,7 ], + "compact": [1,2,3,4,5,6,7], + "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}", + "quotes": "" \u0022 %22 0x22 034 "", + "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?" +: "A key can be any string" + }, + 0.5 ,98.6 +, +99.44 +, + +1066 + + +,"rosebud"] + +DECODE: AS OBJECT +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + object(stdClass)#1 (1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + object(stdClass)#2 (0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + object(stdClass)#3 (36) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + ["_empty_"]=> + float(INF) + ["E no ."]=> + float(4.0E+12) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " + + " + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["unicode"]=> + string(18) "プレスキット" + ["プレスキット"]=> + string(18) "プレスキット" + ["empty_string"]=> + string(0) "" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + object(stdClass)#4 (0) { + } + ["123"]=> + object(stdClass)#5 (1) { + ["456"]=> + object(stdClass)#6 (1) { + ["abc"]=> + object(stdClass)#7 (3) { + ["789"]=> + string(3) "def" + ["012"]=> + array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(1) "5" + [3]=> + int(500) + } + ["ghi"]=> + array(5) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(4) "five" + [3]=> + int(50) + [4]=> + string(5) "sixty" + } + } + } + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 + + `1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} +DECODE: AS ARRAY +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + array(1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + array(0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + array(36) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + ["_empty_"]=> + float(INF) + ["E no ."]=> + float(4.0E+12) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " + + " + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["unicode"]=> + string(18) "プレスキット" + ["プレスキット"]=> + string(18) "プレスキット" + ["empty_string"]=> + string(0) "" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + array(0) { + } + [123]=> + array(1) { + [456]=> + array(1) { + ["abc"]=> + array(3) { + [789]=> + string(3) "def" + ["012"]=> + array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(1) "5" + [3]=> + int(500) + } + ["ghi"]=> + array(5) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(4) "five" + [3]=> + int(50) + [4]=> + string(5) "sixty" + } + } + } + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 + + `1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} +ENCODE: FROM OBJECT +["JSON Test Pattern pass1",{"object with 1 member":["array with 1 element"]},{},[],-42,true,false,null,{"integer":1234567890,"real":-9876.54321,"e":1.23456789e-13,"E":1.23456789e+34,"_empty_":0,"E no .":4.0e+12,"zero":0,"one":1,"space":" ","quote":"\"","backslash":"\\","controls":"\b\f\n\r\t","slash":"\/ & \/","alpha":"abcdefghijklmnopqrstuvwyz","ALPHA":"ABCDEFGHIJKLMNOPQRSTUVWYZ","digit":"0123456789","special":"`1~!@#$%^&*()_+-={':[,]}|;.<\/>?","hex":"\u0123\u4567\u89ab\ucdef\uabcd\uef4a","unicode":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","empty_string":"","true":true,"false":false,"null":null,"array":[],"object":{},"123":{"456":{"abc":{"789":"def","012":[1,2,"5",500],"ghi":[1,2,"five",50,"sixty"]}}},"address":"50 St. James Street","url":"http:\/\/www.JSON.org\/","comment":"\/\/ \/* <!-- --","# -- --> *\/":" "," s p a c e d ":[1,2,3,4,5,6,7],"compact":[1,2,3,4,5,6,7],"jsontext":"{\"object with 1 member\":[\"array with 1 element\"]}","quotes":"" \" %22 0x22 034 "","\/\\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',.\/<>?":"A key can be any string"},0.5,98.6,99.44,1066,"rosebud"] +ENCODE: FROM ARRAY +["JSON Test Pattern pass1",{"object with 1 member":["array with 1 element"]},[],[],-42,true,false,null,{"integer":1234567890,"real":-9876.54321,"e":1.23456789e-13,"E":1.23456789e+34,"_empty_":0,"E no .":4.0e+12,"zero":0,"one":1,"space":" ","quote":"\"","backslash":"\\","controls":"\b\f\n\r\t","slash":"\/ & \/","alpha":"abcdefghijklmnopqrstuvwyz","ALPHA":"ABCDEFGHIJKLMNOPQRSTUVWYZ","digit":"0123456789","special":"`1~!@#$%^&*()_+-={':[,]}|;.<\/>?","hex":"\u0123\u4567\u89ab\ucdef\uabcd\uef4a","unicode":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8":"\u30d7\u30ec\u30b9\u30ad\u30c3\u30c8","empty_string":"","true":true,"false":false,"null":null,"array":[],"object":[],"123":{"456":{"abc":{"789":"def","012":[1,2,"5",500],"ghi":[1,2,"five",50,"sixty"]}}},"address":"50 St. James Street","url":"http:\/\/www.JSON.org\/","comment":"\/\/ \/* <!-- --","# -- --> *\/":" "," s p a c e d ":[1,2,3,4,5,6,7],"compact":[1,2,3,4,5,6,7],"jsontext":"{\"object with 1 member\":[\"array with 1 element\"]}","quotes":"" \" %22 0x22 034 "","\/\\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',.\/<>?":"A key can be any string"},0.5,98.6,99.44,1066,"rosebud"] +DECODE AGAIN: AS OBJECT +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + object(stdClass)#8 (1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + object(stdClass)#9 (0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + object(stdClass)#10 (36) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + ["_empty_"]=> + int(0) + ["E no ."]=> + float(4.0E+12) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " + + " + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["unicode"]=> + string(18) "プレスキット" + ["プレスキット"]=> + string(18) "プレスキット" + ["empty_string"]=> + string(0) "" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + object(stdClass)#11 (0) { + } + ["123"]=> + object(stdClass)#12 (1) { + ["456"]=> + object(stdClass)#13 (1) { + ["abc"]=> + object(stdClass)#14 (3) { + ["789"]=> + string(3) "def" + ["012"]=> + array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(1) "5" + [3]=> + int(500) + } + ["ghi"]=> + array(5) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(4) "five" + [3]=> + int(50) + [4]=> + string(5) "sixty" + } + } + } + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 + + `1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} +DECODE AGAIN: AS ARRAY +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + array(1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + array(0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + array(36) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + ["_empty_"]=> + int(0) + ["E no ."]=> + float(4.0E+12) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " + + " + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["unicode"]=> + string(18) "プレスキット" + ["プレスキット"]=> + string(18) "プレスキット" + ["empty_string"]=> + string(0) "" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + array(0) { + } + [123]=> + array(1) { + [456]=> + array(1) { + ["abc"]=> + array(3) { + [789]=> + string(3) "def" + ["012"]=> + array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(1) "5" + [3]=> + int(500) + } + ["ghi"]=> + array(5) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(4) "five" + [3]=> + int(50) + [4]=> + string(5) "sixty" + } + } + } + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 + + `1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} diff --git a/ext/json/tests/pass001.phpt b/ext/json/tests/pass001.phpt new file mode 100644 index 0000000000..aff970b107 --- /dev/null +++ b/ext/json/tests/pass001.phpt @@ -0,0 +1,709 @@ +--TEST-- +JSON Test Pattern pass1 +http://www.crockford.com/JSON/JSON_checker/test/pass1.json +--SKIPIF-- +<?php + if (!extension_loaded('json')) die('skip: json extension not available'); +?> +--FILE-- +<?php +// Expect warnings about INF. +ini_set("error_reporting", E_ALL & ~E_WARNING); + +$test = " +[ + \"JSON Test Pattern pass1\", + {\"object with 1 member\":[\"array with 1 element\"]}, + {}, + [], + -42, + true, + false, + null, + { + \"integer\": 1234567890, + \"real\": -9876.543210, + \"e\": 0.123456789e-12, + \"E\": 1.234567890E+34, + \"\": 23456789012E666, + \"zero\": 0, + \"one\": 1, + \"space\": \" \", + \"quote\": \"\\\"\", + \"backslash\": \"\\\\\", + \"controls\": \"\\b\\f\\n\\r\\t\", + \"slash\": \"/ & \\/\", + \"alpha\": \"abcdefghijklmnopqrstuvwyz\", + \"ALPHA\": \"ABCDEFGHIJKLMNOPQRSTUVWYZ\", + \"digit\": \"0123456789\", + \"special\": \"`1~!@#$%^&*()_+-={':[,]}|;.</>?\", + \"hex\": \"\\u0123\\u4567\\u89AB\\uCDEF\\uabcd\\uef4A\", + \"true\": true, + \"false\": false, + \"null\": null, + \"array\":[ ], + \"object\":{ }, + \"address\": \"50 St. James Street\", + \"url\": \"http://www.JSON.org/\", + \"comment\": \"// /* <!-- --\", + \"# -- --> */\": \" \", + \" s p a c e d \" :[1,2 , 3 + +, + +4 , 5 , 6 ,7 ], + \"compact\": [1,2,3,4,5,6,7], + \"jsontext\": \"{\\\"object with 1 member\\\":[\\\"array with 1 element\\\"]}\", + \"quotes\": \"" \\u0022 %22 0x22 034 "\", + \"\\/\\\\\\\"\\uCAFE\\uBABE\\uAB98\\uFCDE\\ubcda\\uef4A\\b\\f\\n\\r\\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?\" +: \"A key can be any string\" + }, + 0.5 ,98.6 +, +99.44 +, + +1066 + + +,\"rosebud\"] +"; + +echo 'Testing: ' . $test . "\n"; +echo "DECODE: AS OBJECT\n"; +$obj = json_decode($test); +var_dump($obj); +echo "DECODE: AS ARRAY\n"; +$arr = json_decode($test, true); +var_dump($arr); + +echo "ENCODE: FROM OBJECT\n"; +$obj_enc = json_encode($obj); +echo $obj_enc . "\n"; +echo "ENCODE: FROM ARRAY\n"; +$arr_enc = json_encode($arr); +echo $arr_enc . "\n"; + +echo "DECODE AGAIN: AS OBJECT\n"; +$obj = json_decode($obj_enc); +var_dump($obj); +echo "DECODE AGAIN: AS ARRAY\n"; +$arr = json_decode($arr_enc, true); +var_dump($arr); + +?> +--EXPECT-- +Testing: +[ + "JSON Test Pattern pass1", + {"object with 1 member":["array with 1 element"]}, + {}, + [], + -42, + true, + false, + null, + { + "integer": 1234567890, + "real": -9876.543210, + "e": 0.123456789e-12, + "E": 1.234567890E+34, + "": 23456789012E666, + "zero": 0, + "one": 1, + "space": " ", + "quote": "\"", + "backslash": "\\", + "controls": "\b\f\n\r\t", + "slash": "/ & \/", + "alpha": "abcdefghijklmnopqrstuvwyz", + "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ", + "digit": "0123456789", + "special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?", + "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A", + "true": true, + "false": false, + "null": null, + "array":[ ], + "object":{ }, + "address": "50 St. James Street", + "url": "http://www.JSON.org/", + "comment": "// /* <!-- --", + "# -- --> */": " ", + " s p a c e d " :[1,2 , 3 + +, + +4 , 5 , 6 ,7 ], + "compact": [1,2,3,4,5,6,7], + "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}", + "quotes": "" \u0022 %22 0x22 034 "", + "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?" +: "A key can be any string" + }, + 0.5 ,98.6 +, +99.44 +, + +1066 + + +,"rosebud"] + +DECODE: AS OBJECT +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + object(stdClass)#1 (1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + object(stdClass)#2 (0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + object(stdClass)#3 (31) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + ["_empty_"]=> + float(INF) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " + + " + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + object(stdClass)#4 (0) { + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 + + `1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} +DECODE: AS ARRAY +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + array(1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + array(0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + array(31) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + ["_empty_"]=> + float(INF) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " + + " + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + array(0) { + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 + + `1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} +ENCODE: FROM OBJECT +["JSON Test Pattern pass1",{"object with 1 member":["array with 1 element"]},{},[],-42,true,false,null,{"integer":1234567890,"real":-9876.54321,"e":1.23456789e-13,"E":1.23456789e+34,"_empty_":0,"zero":0,"one":1,"space":" ","quote":"\"","backslash":"\\","controls":"\b\f\n\r\t","slash":"\/ & \/","alpha":"abcdefghijklmnopqrstuvwyz","ALPHA":"ABCDEFGHIJKLMNOPQRSTUVWYZ","digit":"0123456789","special":"`1~!@#$%^&*()_+-={':[,]}|;.<\/>?","hex":"\u0123\u4567\u89ab\ucdef\uabcd\uef4a","true":true,"false":false,"null":null,"array":[],"object":{},"address":"50 St. James Street","url":"http:\/\/www.JSON.org\/","comment":"\/\/ \/* <!-- --","# -- --> *\/":" "," s p a c e d ":[1,2,3,4,5,6,7],"compact":[1,2,3,4,5,6,7],"jsontext":"{\"object with 1 member\":[\"array with 1 element\"]}","quotes":"" \" %22 0x22 034 "","\/\\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',.\/<>?":"A key can be any string"},0.5,98.6,99.44,1066,"rosebud"] +ENCODE: FROM ARRAY +["JSON Test Pattern pass1",{"object with 1 member":["array with 1 element"]},[],[],-42,true,false,null,{"integer":1234567890,"real":-9876.54321,"e":1.23456789e-13,"E":1.23456789e+34,"_empty_":0,"zero":0,"one":1,"space":" ","quote":"\"","backslash":"\\","controls":"\b\f\n\r\t","slash":"\/ & \/","alpha":"abcdefghijklmnopqrstuvwyz","ALPHA":"ABCDEFGHIJKLMNOPQRSTUVWYZ","digit":"0123456789","special":"`1~!@#$%^&*()_+-={':[,]}|;.<\/>?","hex":"\u0123\u4567\u89ab\ucdef\uabcd\uef4a","true":true,"false":false,"null":null,"array":[],"object":[],"address":"50 St. James Street","url":"http:\/\/www.JSON.org\/","comment":"\/\/ \/* <!-- --","# -- --> *\/":" "," s p a c e d ":[1,2,3,4,5,6,7],"compact":[1,2,3,4,5,6,7],"jsontext":"{\"object with 1 member\":[\"array with 1 element\"]}","quotes":"" \" %22 0x22 034 "","\/\\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',.\/<>?":"A key can be any string"},0.5,98.6,99.44,1066,"rosebud"] +DECODE AGAIN: AS OBJECT +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + object(stdClass)#5 (1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + object(stdClass)#6 (0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + object(stdClass)#7 (31) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + ["_empty_"]=> + int(0) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " + + " + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + object(stdClass)#8 (0) { + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 + + `1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} +DECODE AGAIN: AS ARRAY +array(14) { + [0]=> + string(23) "JSON Test Pattern pass1" + [1]=> + array(1) { + ["object with 1 member"]=> + array(1) { + [0]=> + string(20) "array with 1 element" + } + } + [2]=> + array(0) { + } + [3]=> + array(0) { + } + [4]=> + int(-42) + [5]=> + bool(true) + [6]=> + bool(false) + [7]=> + NULL + [8]=> + array(31) { + ["integer"]=> + int(1234567890) + ["real"]=> + float(-9876.54321) + ["e"]=> + float(1.23456789E-13) + ["E"]=> + float(1.23456789E+34) + ["_empty_"]=> + int(0) + ["zero"]=> + int(0) + ["one"]=> + int(1) + ["space"]=> + string(1) " " + ["quote"]=> + string(1) """ + ["backslash"]=> + string(1) "\" + ["controls"]=> + string(5) " + + " + ["slash"]=> + string(5) "/ & /" + ["alpha"]=> + string(25) "abcdefghijklmnopqrstuvwyz" + ["ALPHA"]=> + string(25) "ABCDEFGHIJKLMNOPQRSTUVWYZ" + ["digit"]=> + string(10) "0123456789" + ["special"]=> + string(31) "`1~!@#$%^&*()_+-={':[,]}|;.</>?" + ["hex"]=> + string(17) "ģ䕧覫췯ꯍ" + ["true"]=> + bool(true) + ["false"]=> + bool(false) + ["null"]=> + NULL + ["array"]=> + array(0) { + } + ["object"]=> + array(0) { + } + ["address"]=> + string(19) "50 St. James Street" + ["url"]=> + string(20) "http://www.JSON.org/" + ["comment"]=> + string(13) "// /* <!-- --" + ["# -- --> */"]=> + string(1) " " + [" s p a c e d "]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["compact"]=> + array(7) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(4) + [4]=> + int(5) + [5]=> + int(6) + [6]=> + int(7) + } + ["jsontext"]=> + string(49) "{"object with 1 member":["array with 1 element"]}" + ["quotes"]=> + string(27) "" " %22 0x22 034 "" + ["/\"쫾몾ꮘﳞ볚 + + `1~!@#$%^&*()_+-=[]{}|;:',./<>?"]=> + string(23) "A key can be any string" + } + [9]=> + float(0.5) + [10]=> + float(98.6) + [11]=> + float(99.44) + [12]=> + int(1066) + [13]=> + string(7) "rosebud" +} diff --git a/ext/json/tests/pass002.phpt b/ext/json/tests/pass002.phpt new file mode 100644 index 0000000000..a0e527ac4f --- /dev/null +++ b/ext/json/tests/pass002.phpt @@ -0,0 +1,276 @@ +--TEST-- +JSON Test Pattern pass2 +http://www.crockford.com/JSON/JSON_checker/test/pass2.json +--SKIPIF-- +<?php + if (!extension_loaded('json')) die('skip: json extension not available'); +?> +--FILE-- +<?php + +$test = '[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]]'; +echo 'Testing: ' . $test . "\n"; +echo "DECODE: AS OBJECT\n"; +$obj = json_decode($test); +var_dump($obj); +echo "DECODE: AS ARRAY\n"; +$arr = json_decode($test, true); +var_dump($arr); + +echo "ENCODE: FROM OBJECT\n"; +$obj_enc = json_encode($obj); +echo $obj_enc . "\n"; +echo "ENCODE: FROM ARRAY\n"; +$arr_enc = json_encode($arr); +echo $arr_enc . "\n"; + +echo "DECODE AGAIN: AS OBJECT\n"; +$obj = json_decode($obj_enc); +var_dump($obj); +echo "DECODE AGAIN: AS ARRAY\n"; +$arr = json_decode($arr_enc, true); +var_dump($arr); + +?> +--EXPECT-- +Testing: [[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]] +DECODE: AS OBJECT +array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + string(12) "Not too deep" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} +DECODE: AS ARRAY +array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + string(12) "Not too deep" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} +ENCODE: FROM OBJECT +[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]] +ENCODE: FROM ARRAY +[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]] +DECODE AGAIN: AS OBJECT +array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + string(12) "Not too deep" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} +DECODE AGAIN: AS ARRAY +array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + string(12) "Not too deep" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} diff --git a/ext/json/tests/pass003.phpt b/ext/json/tests/pass003.phpt new file mode 100644 index 0000000000..506ff05b3e --- /dev/null +++ b/ext/json/tests/pass003.phpt @@ -0,0 +1,95 @@ +--TEST-- +JSON Test Pattern pass3 +http://www.crockford.com/JSON/JSON_checker/test/pass3.json +--SKIPIF-- +<?php + if (!extension_loaded('json')) die('skip: json extension not available'); +?> +--FILE-- +<?php + +$test = ' +{ + "JSON Test Pattern pass3": { + "The outermost value": "must be an object or array.", + "In this test": "It is an object." + } +} +'; + +echo 'Testing: ' . $test . "\n"; +echo "DECODE: AS OBJECT\n"; +$obj = json_decode($test); +var_dump($obj); +echo "DECODE: AS ARRAY\n"; +$arr = json_decode($test, true); +var_dump($arr); + +echo "ENCODE: FROM OBJECT\n"; +$obj_enc = json_encode($obj); +echo $obj_enc . "\n"; +echo "ENCODE: FROM ARRAY\n"; +$arr_enc = json_encode($arr); +echo $arr_enc . "\n"; + +echo "DECODE AGAIN: AS OBJECT\n"; +$obj = json_decode($obj_enc); +var_dump($obj); +echo "DECODE AGAIN: AS ARRAY\n"; +$arr = json_decode($arr_enc, true); +var_dump($arr); + +?> +--EXPECT-- +Testing: +{ + "JSON Test Pattern pass3": { + "The outermost value": "must be an object or array.", + "In this test": "It is an object." + } +} + +DECODE: AS OBJECT +object(stdClass)#1 (1) { + ["JSON Test Pattern pass3"]=> + object(stdClass)#2 (2) { + ["The outermost value"]=> + string(27) "must be an object or array." + ["In this test"]=> + string(16) "It is an object." + } +} +DECODE: AS ARRAY +array(1) { + ["JSON Test Pattern pass3"]=> + array(2) { + ["The outermost value"]=> + string(27) "must be an object or array." + ["In this test"]=> + string(16) "It is an object." + } +} +ENCODE: FROM OBJECT +{"JSON Test Pattern pass3":{"The outermost value":"must be an object or array.","In this test":"It is an object."}} +ENCODE: FROM ARRAY +{"JSON Test Pattern pass3":{"The outermost value":"must be an object or array.","In this test":"It is an object."}} +DECODE AGAIN: AS OBJECT +object(stdClass)#3 (1) { + ["JSON Test Pattern pass3"]=> + object(stdClass)#4 (2) { + ["The outermost value"]=> + string(27) "must be an object or array." + ["In this test"]=> + string(16) "It is an object." + } +} +DECODE AGAIN: AS ARRAY +array(1) { + ["JSON Test Pattern pass3"]=> + array(2) { + ["The outermost value"]=> + string(27) "must be an object or array." + ["In this test"]=> + string(16) "It is an object." + } +} diff --git a/ext/json/utf8_decode.c b/ext/json/utf8_decode.c new file mode 100644 index 0000000000..cea1f8cec8 --- /dev/null +++ b/ext/json/utf8_decode.c @@ -0,0 +1,179 @@ +/* utf8_decode.c */ + +/* 2005-12-25 */ + +/* +Copyright (c) 2005 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "utf8_decode.h" + +/* + Very Strict UTF-8 Decoder + + UTF-8 is a multibyte character encoding of Unicode. A character can be + represented by 1-4 bytes. The bit pattern of the first byte indicates the + number of continuation bytes. + + Most UTF-8 decoders tend to be lenient, attempting to recover as much + information as possible, even from badly encoded input. This UTF-8 + decoder is not lenient. It will reject input which does not include + proper continuation bytes. It will reject aliases (or suboptimal + codings). It will reject surrogates. (Surrogate encoding should only be + used with UTF-16.) + + Code Contination Minimum Maximum + 0xxxxxxx 0 0 127 + 10xxxxxx error + 110xxxxx 1 128 2047 + 1110xxxx 2 2048 65535 excluding 55296 - 57343 + 11110xxx 3 65536 1114111 + 11111xxx error +*/ + + +/* + Get the next byte. It returns UTF8_END if there are no more bytes. +*/ +static int +get(json_utf8_decode *utf8) +{ + int c; + if (utf8->the_index >= utf8->the_length) { + return UTF8_END; + } + c = utf8->the_input[utf8->the_index] & 0xFF; + utf8->the_index += 1; + return c; +} + + +/* + Get the 6-bit payload of the next continuation byte. + Return UTF8_ERROR if it is not a contination byte. +*/ +static int +cont(json_utf8_decode *utf8) +{ + int c = get(utf8); + return ((c & 0xC0) == 0x80) ? (c & 0x3F) : UTF8_ERROR; +} + + +/* + Initialize the UTF-8 decoder. The decoder is not reentrant, +*/ +void +utf8_decode_init(json_utf8_decode *utf8, char p[], int length) +{ + utf8->the_index = 0; + utf8->the_input = p; + utf8->the_length = length; + utf8->the_char = 0; + utf8->the_byte = 0; +} + + +/* + Get the current byte offset. This is generally used in error reporting. +*/ +int +utf8_decode_at_byte(json_utf8_decode *utf8) +{ + return utf8->the_byte; +} + + +/* + Get the current character offset. This is generally used in error reporting. + The character offset matches the byte offset if the text is strictly ASCII. +*/ +int +utf8_decode_at_character(json_utf8_decode *utf8) +{ + return utf8->the_char > 0 ? utf8->the_char - 1 : 0; +} + + +/* + Extract the next character. + Returns: the character (between 0 and 1114111) + or UTF8_END (the end) + or UTF8_ERROR (error) +*/ +int +utf8_decode_next(json_utf8_decode *utf8) +{ + int c; /* the first byte of the character */ + int r; /* the result */ + + if (utf8->the_index >= utf8->the_length) { + return utf8->the_index == utf8->the_length ? UTF8_END : UTF8_ERROR; + } + utf8->the_byte = utf8->the_index; + utf8->the_char += 1; + c = get(utf8); +/* + Zero continuation (0 to 127) +*/ + if ((c & 0x80) == 0) { + return c; + } +/* + One contination (128 to 2047) +*/ + if ((c & 0xE0) == 0xC0) { + int c1 = cont(utf8); + if (c1 < 0) { + return UTF8_ERROR; + } + r = ((c & 0x1F) << 6) | c1; + return r >= 128 ? r : UTF8_ERROR; + } +/* + Two continuation (2048 to 55295 and 57344 to 65535) +*/ + if ((c & 0xF0) == 0xE0) { + int c1 = cont(utf8); + int c2 = cont(utf8); + if (c1 < 0 || c2 < 0) { + return UTF8_ERROR; + } + r = ((c & 0x0F) << 12) | (c1 << 6) | c2; + return r >= 2048 && (r < 55296 || r > 57343) ? r : UTF8_ERROR; + } +/* + Three continuation (65536 to 1114111) +*/ + if ((c & 0xF1) == 0xF0) { + int c1 = cont(utf8); + int c2 = cont(utf8); + int c3 = cont(utf8); + if (c1 < 0 || c2 < 0 || c3 < 0) { + return UTF8_ERROR; + } + r = ((c & 0x0F) << 18) | (c1 << 12) | (c2 << 6) | c3; + return r >= 65536 && r <= 1114111 ? r : UTF8_ERROR; + } + return UTF8_ERROR; +} diff --git a/ext/json/utf8_decode.h b/ext/json/utf8_decode.h new file mode 100644 index 0000000000..cc0fc79f6c --- /dev/null +++ b/ext/json/utf8_decode.h @@ -0,0 +1,18 @@ +/* utf8_decode.h */ + +#define UTF8_END -1 +#define UTF8_ERROR -2 + +typedef struct json_utf8_decode +{ + int the_index; + char *the_input; + int the_length; + int the_char; + int the_byte; +} json_utf8_decode; + +extern int utf8_decode_at_byte(json_utf8_decode *utf8); +extern int utf8_decode_at_character(json_utf8_decode *utf8); +extern void utf8_decode_init(json_utf8_decode *utf8, char p[], int length); +extern int utf8_decode_next(json_utf8_decode *utf8); diff --git a/ext/json/utf8_to_utf16.c b/ext/json/utf8_to_utf16.c new file mode 100644 index 0000000000..bc2d6f36d6 --- /dev/null +++ b/ext/json/utf8_to_utf16.c @@ -0,0 +1,56 @@ +/* utf8_to_utf16.c */ + +/* 2005-12-25 */ + +/* +Copyright (c) 2005 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "utf8_to_utf16.h" +#include "utf8_decode.h" + +int +utf8_to_utf16(unsigned short w[], char p[], int length) +{ + int c; + int the_index = 0; + json_utf8_decode utf8; + + utf8_decode_init(&utf8, p, length); + for (;;) { + c = utf8_decode_next(&utf8); + if (c < 0) { + return UTF8_END ? the_index : UTF8_ERROR; + } + if (c < 0x10000) { + w[the_index] = (unsigned short)c; + the_index += 1; + } else { + c &= 0xFFFF; + w[the_index] = (unsigned short)(0xD800 | (c >> 10)); + the_index += 1; + w[the_index] = (unsigned short)(0xDC00 | (c & 0x3FF)); + the_index += 1; + } + } +} diff --git a/ext/json/utf8_to_utf16.h b/ext/json/utf8_to_utf16.h new file mode 100644 index 0000000000..5aff0268bf --- /dev/null +++ b/ext/json/utf8_to_utf16.h @@ -0,0 +1,3 @@ +/* utf8_to_utf16.h */ + +extern int utf8_to_utf16(unsigned short w[], char p[], int length); |