diff options
-rw-r--r-- | ext/json/JSON_parser.c | 30 | ||||
-rw-r--r-- | ext/json/JSON_parser.h | 10 | ||||
-rw-r--r-- | ext/json/json.c | 49 | ||||
-rw-r--r-- | ext/json/php_json.h | 10 | ||||
-rw-r--r-- | ext/json/tests/007.phpt | 36 |
5 files changed, 107 insertions, 28 deletions
diff --git a/ext/json/JSON_parser.c b/ext/json/JSON_parser.c index 95a7ffba1f..edb1adf005 100644 --- a/ext/json/JSON_parser.c +++ b/ext/json/JSON_parser.c @@ -194,13 +194,6 @@ enum modes { MODE_OBJECT, }; -enum error_codes { - ERROR_DEPTH, - ERROR_MISMATCH, - ERROR_CTRL_CHAR, - ERROR_SYNTAX, -}; - /* Push a mode onto the stack. Return false if there is overflow. @@ -210,7 +203,7 @@ push(JSON_parser jp, int mode) { jp->top += 1; if (jp->top >= jp->depth) { - jp->error = ERROR_DEPTH; + jp->error_code = PHP_JSON_ERROR_DEPTH; return false; } jp->stack[jp->top] = mode; @@ -226,7 +219,7 @@ static int pop(JSON_parser jp, int mode) { if (jp->top < 0 || jp->stack[jp->top] != mode) { - jp->error = ERROR_MISMATCH; + jp->error_code = PHP_JSON_ERROR_STATE_MISMATCH; return false; } jp->top -= 1; @@ -252,6 +245,7 @@ new_JSON_parser(int depth) jp->state = GO; jp->depth = depth; jp->top = -1; + jp->error_code = PHP_JSON_ERROR_NONE; jp->stack = (int*)ecalloc(depth, sizeof(int)); push(jp, MODE_DONE); return jp; @@ -428,7 +422,7 @@ parse_JSON(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int } else { next_class = ascii_class[next_char]; if (next_class <= __) { - jp->error = ERROR_CTRL_CHAR; + jp->error_code = PHP_JSON_ERROR_CTRL_CHAR; FREE_BUFFERS(); return false; } @@ -511,9 +505,7 @@ parse_JSON(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int break; /* } */ case -8: - if (type != -1 && - (jp->stack[jp->top] == MODE_OBJECT || - jp->stack[jp->top] == MODE_ARRAY)) + if (type != -1 && jp->stack[jp->top] == MODE_OBJECT) { zval *mval; smart_str_0(&buf); @@ -541,9 +533,7 @@ parse_JSON(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int /* ] */ case -7: { - if (type != -1 && - (jp->stack[jp->top] == MODE_OBJECT || - jp->stack[jp->top] == MODE_ARRAY)) + if (type != -1 && jp->stack[jp->top] == MODE_ARRAY) { zval *mval; smart_str_0(&buf); @@ -702,7 +692,7 @@ parse_JSON(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int */ default: { - jp->error = ERROR_SYNTAX; + jp->error_code = PHP_JSON_ERROR_SYNTAX; FREE_BUFFERS(); return false; } @@ -711,8 +701,12 @@ parse_JSON(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int } FREE_BUFFERS(); + if (jp->state == OK && pop(jp, MODE_DONE)) { + return true; + } - return jp->state == OK && pop(jp, MODE_DONE); + jp->error_code = PHP_JSON_ERROR_SYNTAX; + return false; } diff --git a/ext/json/JSON_parser.h b/ext/json/JSON_parser.h index 9aab62a912..0ed7e52a8f 100644 --- a/ext/json/JSON_parser.h +++ b/ext/json/JSON_parser.h @@ -12,12 +12,20 @@ typedef struct JSON_parser_struct { int state; int depth; int top; - int error; + int error_code; int* stack; zval *the_zstack[JSON_PARSER_MAX_DEPTH]; } * JSON_parser; +enum error_codes { + PHP_JSON_ERROR_NONE = 0, + PHP_JSON_ERROR_DEPTH, + PHP_JSON_ERROR_STATE_MISMATCH, + PHP_JSON_ERROR_CTRL_CHAR, + PHP_JSON_ERROR_SYNTAX, +}; + extern JSON_parser new_JSON_parser(int depth); extern int parse_JSON(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int assoc TSRMLS_DC); extern int free_JSON_parser(JSON_parser jp); diff --git a/ext/json/json.c b/ext/json/json.c index e09027576b..b3049d002d 100644 --- a/ext/json/json.c +++ b/ext/json/json.c @@ -33,6 +33,7 @@ static PHP_MINFO_FUNCTION(json); static PHP_FUNCTION(json_encode); static PHP_FUNCTION(json_decode); +static PHP_FUNCTION(json_last_error); static const char digits[] = "0123456789abcdef"; @@ -41,6 +42,8 @@ static const char digits[] = "0123456789abcdef"; #define PHP_JSON_HEX_APOS (1<<2) #define PHP_JSON_HEX_QUOT (1<<3) +ZEND_DECLARE_MODULE_GLOBALS(json) + /* {{{ arginfo */ ZEND_BEGIN_ARG_INFO_EX(arginfo_json_encode, 0, 0, 1) ZEND_ARG_INFO(0, value) @@ -51,15 +54,16 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_json_decode, 0, 0, 1) ZEND_ARG_INFO(0, json) ZEND_ARG_INFO(0, assoc) ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_json_last_error, 0) +ZEND_END_ARG_INFO() /* }}} */ -/* {{{ json_functions[] - * - * Every user visible function must have an entry in json_functions[]. - */ +/* {{{ json_functions[] */ static const function_entry json_functions[] = { PHP_FE(json_encode, arginfo_json_encode) PHP_FE(json_decode, arginfo_json_decode) + PHP_FE(json_last_error, arginfo_json_last_error) {NULL, NULL, NULL} }; /* }}} */ @@ -72,17 +76,29 @@ static PHP_MINIT_FUNCTION(json) REGISTER_LONG_CONSTANT("JSON_HEX_APOS", PHP_JSON_HEX_APOS, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("JSON_HEX_QUOT", PHP_JSON_HEX_QUOT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_ERROR_NONE", PHP_JSON_ERROR_NONE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_ERROR_DEPTH", PHP_JSON_ERROR_DEPTH, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_ERROR_STATE_MISMATCH", PHP_JSON_ERROR_STATE_MISMATCH, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_ERROR_CTRL_CHAR", PHP_JSON_ERROR_CTRL_CHAR, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_ERROR_SYNTAX", PHP_JSON_ERROR_SYNTAX, CONST_CS | CONST_PERSISTENT); + return SUCCESS; } /* }}} */ +/* {{{ PHP_GINIT_FUNCTION +*/ +static PHP_GINIT_FUNCTION(json) +{ + json_globals->error_code = 0; +} +/* }}} */ + /* {{{ json_module_entry */ zend_module_entry json_module_entry = { -#if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, -#endif "json", json_functions, PHP_MINIT(json), @@ -90,10 +106,12 @@ zend_module_entry json_module_entry = { NULL, NULL, PHP_MINFO(json), -#if ZEND_MODULE_API_NO >= 20010901 PHP_JSON_VERSION, -#endif - STANDARD_MODULE_PROPERTIES + PHP_MODULE_GLOBALS(json), + PHP_GINIT(json), + NULL, + NULL, + STANDARD_MODULE_PROPERTIES_EX }; /* }}} */ @@ -533,10 +551,23 @@ static PHP_FUNCTION(json_decode) } FREE_ZVAL(z); efree(utf16); + JSON_G(error_code) = jp->error_code; free_JSON_parser(jp); } /* }}} */ +/* {{{ proto int json_last_error() + Returns the error code of the last json_decode(). */ +static PHP_FUNCTION(json_last_error) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + RETURN_LONG(JSON_G(error_code)); +} +/* }}} */ + /* * Local variables: * tab-width: 4 diff --git a/ext/json/php_json.h b/ext/json/php_json.h index dfc9427622..8b86a99f71 100644 --- a/ext/json/php_json.h +++ b/ext/json/php_json.h @@ -30,6 +30,16 @@ extern zend_module_entry json_module_entry; #include "TSRM.h" #endif +ZEND_BEGIN_MODULE_GLOBALS(json) + int error_code; +ZEND_END_MODULE_GLOBALS(json) + +#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 */ /* diff --git a/ext/json/tests/007.phpt b/ext/json/tests/007.phpt new file mode 100644 index 0000000000..9ee190a24c --- /dev/null +++ b/ext/json/tests/007.phpt @@ -0,0 +1,36 @@ +--TEST-- +json_last_error() tests +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php +var_dump(json_decode("[1]")); +var_dump(json_last_error()); +var_dump(json_decode("[[1]]", false, 2)); +var_dump(json_last_error()); +var_dump(json_decode("[1}")); +var_dump(json_last_error()); +var_dump(json_decode('["' . chr(0) . 'abcd"]')); +var_dump(json_last_error()); +var_dump(json_decode("[1")); +var_dump(json_last_error()); + + +echo "Done\n"; +?> +--EXPECT-- +array(1) { + [0]=> + int(1) +} +int(0) +NULL +int(1) +NULL +int(2) +NULL +int(3) +NULL +int(4) +Done + |