diff --git a/ext/json/json.c b/ext/json/json.c index 9f91d39594ec7..1c7e3219026eb 100644 --- a/ext/json/json.c +++ b/ext/json/json.c @@ -63,6 +63,8 @@ static PHP_GINIT_FUNCTION(json) #endif json_globals->encoder_depth = 0; json_globals->error_code = 0; + json_globals->error_line = 0; + json_globals->error_column = 0; json_globals->encode_max_depth = PHP_JSON_PARSER_DEFAULT_DEPTH; } /* }}} */ @@ -70,6 +72,8 @@ static PHP_GINIT_FUNCTION(json) static PHP_RINIT_FUNCTION(json) { JSON_G(error_code) = 0; + JSON_G(error_line) = 0; + JSON_G(error_column) = 0; return SUCCESS; } @@ -177,6 +181,18 @@ static const char *php_json_get_error_msg(php_json_error_code error_code) /* {{{ } /* }}} */ +static zend_string *php_json_get_error_msg_with_location(php_json_error_code error_code, size_t line, size_t column) /* {{{ */ +{ + const char *base_msg = php_json_get_error_msg(error_code); + + if (line > 0 && column > 0) { + return zend_strpprintf(0, "%s near location %zu:%zu", base_msg, line, column); + } + + return zend_string_init(base_msg, strlen(base_msg), 0); +} +/* }}} */ + PHP_JSON_API zend_result php_json_decode_ex(zval *return_value, const char *str, size_t str_len, zend_long options, zend_long depth) /* {{{ */ { php_json_parser parser; @@ -185,10 +201,17 @@ PHP_JSON_API zend_result php_json_decode_ex(zval *return_value, const char *str, if (php_json_yyparse(&parser)) { php_json_error_code error_code = php_json_parser_error_code(&parser); + size_t error_line = php_json_parser_error_line(&parser); + size_t error_column = php_json_parser_error_column(&parser); + if (!(options & PHP_JSON_THROW_ON_ERROR)) { JSON_G(error_code) = error_code; + JSON_G(error_line) = error_line; + JSON_G(error_column) = error_column; } else { - zend_throw_exception(php_json_exception_ce, php_json_get_error_msg(error_code), error_code); + zend_string *error_msg = php_json_get_error_msg_with_location(error_code, error_line, error_column); + zend_throw_exception(php_json_exception_ce, ZSTR_VAL(error_msg), error_code); + zend_string_release(error_msg); } RETVAL_NULL(); return FAILURE; @@ -208,7 +231,12 @@ PHP_JSON_API bool php_json_validate_ex(const char *str, size_t str_len, zend_lon if (php_json_yyparse(&parser)) { php_json_error_code error_code = php_json_parser_error_code(&parser); + size_t error_line = php_json_parser_error_line(&parser); + size_t error_column = php_json_parser_error_column(&parser); + JSON_G(error_code) = error_code; + JSON_G(error_line) = error_line; + JSON_G(error_column) = error_column; return false; } @@ -274,11 +302,15 @@ PHP_FUNCTION(json_decode) if (!(options & PHP_JSON_THROW_ON_ERROR)) { JSON_G(error_code) = PHP_JSON_ERROR_NONE; + JSON_G(error_line) = 0; + JSON_G(error_column) = 0; } if (!str_len) { if (!(options & PHP_JSON_THROW_ON_ERROR)) { JSON_G(error_code) = PHP_JSON_ERROR_SYNTAX; + JSON_G(error_line) = 0; + JSON_G(error_column) = 0; } else { zend_throw_exception(php_json_exception_ce, php_json_get_error_msg(PHP_JSON_ERROR_SYNTAX), PHP_JSON_ERROR_SYNTAX); } @@ -331,10 +363,14 @@ PHP_FUNCTION(json_validate) if (!str_len) { JSON_G(error_code) = PHP_JSON_ERROR_SYNTAX; + JSON_G(error_line) = 0; + JSON_G(error_column) = 0; RETURN_FALSE; } JSON_G(error_code) = PHP_JSON_ERROR_NONE; + JSON_G(error_line) = 0; + JSON_G(error_column) = 0; if (depth <= 0) { zend_argument_value_error(2, "must be greater than 0"); @@ -364,6 +400,15 @@ PHP_FUNCTION(json_last_error_msg) { ZEND_PARSE_PARAMETERS_NONE(); - RETURN_STRING(php_json_get_error_msg(JSON_G(error_code))); + if (JSON_G(error_line) > 0 && JSON_G(error_column) > 0) { + zend_string *error_msg = php_json_get_error_msg_with_location( + JSON_G(error_code), + JSON_G(error_line), + JSON_G(error_column) + ); + RETVAL_STR(error_msg); + } else { + RETURN_STRING(php_json_get_error_msg(JSON_G(error_code))); + } } /* }}} */ diff --git a/ext/json/json_parser.y b/ext/json/json_parser.y index d570cddc91e4b..8a8eca871fdec 100644 --- a/ext/json/json_parser.y +++ b/ext/json/json_parser.y @@ -41,6 +41,7 @@ int json_yydebug = 1; } +%locations %define api.prefix {php_json_yy} %define api.pure full %param { php_json_parser *parser } @@ -49,7 +50,6 @@ int json_yydebug = 1; zval value; } - %token PHP_JSON_T_NUL %token PHP_JSON_T_TRUE %token PHP_JSON_T_FALSE @@ -66,8 +66,8 @@ int json_yydebug = 1; %destructor { zval_ptr_dtor_nogc(&$$); } %code { -static int php_json_yylex(union YYSTYPE *value, php_json_parser *parser); -static void php_json_yyerror(php_json_parser *parser, char const *msg); +static int php_json_yylex(union YYSTYPE *value, YYLTYPE *location, php_json_parser *parser); +static void php_json_yyerror(YYLTYPE *location, php_json_parser *parser, char const *msg); static int php_json_parser_array_create(php_json_parser *parser, zval *array); static int php_json_parser_object_create(php_json_parser *parser, zval *array); @@ -277,7 +277,7 @@ static int php_json_parser_object_update_validate(php_json_parser *parser, zval return SUCCESS; } -static int php_json_yylex(union YYSTYPE *value, php_json_parser *parser) +static int php_json_yylex(union YYSTYPE *value, YYLTYPE *location, php_json_parser *parser) { int token = php_json_scan(&parser->scanner); @@ -293,14 +293,24 @@ static int php_json_yylex(union YYSTYPE *value, php_json_parser *parser) value->value = parser->scanner.value; } + location->first_column = PHP_JSON_SCANNER_LOCATION(parser->scanner, first_column); + location->first_line = PHP_JSON_SCANNER_LOCATION(parser->scanner, first_line); + location->last_column = PHP_JSON_SCANNER_LOCATION(parser->scanner, last_column); + location->last_line = PHP_JSON_SCANNER_LOCATION(parser->scanner, last_line); + return token; } -static void php_json_yyerror(php_json_parser *parser, char const *msg) +static void php_json_yyerror(YYLTYPE *location, php_json_parser *parser, char const *msg) { if (!parser->scanner.errcode) { parser->scanner.errcode = PHP_JSON_ERROR_SYNTAX; } + + parser->scanner.errloc.first_column = location->first_column; + parser->scanner.errloc.first_line = location->first_line; + parser->scanner.errloc.last_column = location->last_column; + parser->scanner.errloc.last_line = location->last_line; } PHP_JSON_API php_json_error_code php_json_parser_error_code(const php_json_parser *parser) @@ -308,6 +318,16 @@ PHP_JSON_API php_json_error_code php_json_parser_error_code(const php_json_parse return parser->scanner.errcode; } +PHP_JSON_API size_t php_json_parser_error_line(const php_json_parser *parser) +{ + return parser->scanner.errloc.first_line; +} + +PHP_JSON_API size_t php_json_parser_error_column(const php_json_parser *parser) +{ + return parser->scanner.errloc.first_column; +} + static const php_json_parser_methods default_parser_methods = { php_json_parser_array_create, diff --git a/ext/json/json_scanner.re b/ext/json/json_scanner.re index 0debb3b03cb22..e365c834e2ed9 100644 --- a/ext/json/json_scanner.re +++ b/ext/json/json_scanner.re @@ -52,6 +52,8 @@ #define PHP_JSON_INT_MAX_LENGTH (MAX_LENGTH_OF_LONG - 1) +#define PHP_JSON_TOKEN_LENGTH(s) ((size_t) ((s)->cursor - (s)->token)) +#define PHP_JSON_TOKEN_LOCATION(location) (s)->errloc.location static void php_json_scanner_copy_string(php_json_scanner *s, size_t esc_size) { @@ -96,6 +98,10 @@ void php_json_scanner_init(php_json_scanner *s, const char *str, size_t str_len, s->cursor = (php_json_ctype *) str; s->limit = (php_json_ctype *) str + str_len; s->options = options; + PHP_JSON_TOKEN_LOCATION(first_column) = 1; + PHP_JSON_TOKEN_LOCATION(first_line) = 1; + PHP_JSON_TOKEN_LOCATION(last_column) = 1; + PHP_JSON_TOKEN_LOCATION(last_line) = 1; PHP_JSON_CONDITION_SET(JS); } @@ -104,6 +110,8 @@ int php_json_scan(php_json_scanner *s) ZVAL_NULL(&s->value); std: + PHP_JSON_TOKEN_LOCATION(first_column) = s->errloc.last_column; + PHP_JSON_TOKEN_LOCATION(first_line) = s->errloc.last_line; s->token = s->cursor; /*!re2c @@ -149,27 +157,50 @@ std: UTF16_3 = UTFPREF ( ( ( HEXC | [efEF] ) HEX ) | ( [dD] HEX7 ) ) HEX{2} ; UTF16_4 = UTFPREF [dD] [89abAB] HEX{2} UTFPREF [dD] [c-fC-F] HEX{2} ; - "{" { return '{'; } - "}" { return '}'; } - "[" { return '['; } - "]" { return ']'; } - ":" { return ':'; } - "," { return ','; } + "{" { + PHP_JSON_TOKEN_LOCATION(last_column)++; + return '{'; + } + "}" { + PHP_JSON_TOKEN_LOCATION(last_column)++; + return '}'; + } + "[" { + PHP_JSON_TOKEN_LOCATION(last_column)++; + return '['; + } + "]" { + PHP_JSON_TOKEN_LOCATION(last_column)++; + return ']'; + } + ":" { + PHP_JSON_TOKEN_LOCATION(last_column)++; + return ':'; + } + "," { + PHP_JSON_TOKEN_LOCATION(last_column)++; + return ','; + } "null" { + PHP_JSON_TOKEN_LOCATION(last_column) += 4; ZVAL_NULL(&s->value); return PHP_JSON_T_NUL; } "true" { + PHP_JSON_TOKEN_LOCATION(last_column) += 4; ZVAL_TRUE(&s->value); return PHP_JSON_T_TRUE; } "false" { + PHP_JSON_TOKEN_LOCATION(last_column) += 5; ZVAL_FALSE(&s->value); return PHP_JSON_T_FALSE; } INT { bool bigint = 0, negative = s->token[0] == '-'; - size_t digits = (size_t) (s->cursor - s->token - negative); + size_t digits = PHP_JSON_TOKEN_LENGTH(s); + PHP_JSON_TOKEN_LOCATION(last_column) += digits; + digits -= negative; if (digits >= PHP_JSON_INT_MAX_LENGTH) { if (digits == PHP_JSON_INT_MAX_LENGTH) { int cmp = strncmp((char *) (s->token + negative), LONG_MIN_DIGITS, PHP_JSON_INT_MAX_LENGTH); @@ -192,10 +223,19 @@ std: } } FLOAT|EXP { + PHP_JSON_TOKEN_LOCATION(last_column) += PHP_JSON_TOKEN_LENGTH(s); ZVAL_DOUBLE(&s->value, zend_strtod((char *) s->token, NULL)); return PHP_JSON_T_DOUBLE; } - NL|WS { goto std; } + NL { + PHP_JSON_TOKEN_LOCATION(last_line)++; + PHP_JSON_TOKEN_LOCATION(last_column) = 1; + goto std; + } + WS { + PHP_JSON_TOKEN_LOCATION(last_column) += PHP_JSON_TOKEN_LENGTH(s); + goto std; + } EOI { if (s->limit < s->cursor) { return PHP_JSON_T_EOI; @@ -205,6 +245,7 @@ std: } } ["] { + PHP_JSON_TOKEN_LOCATION(last_column)++; s->str_start = s->cursor; s->str_esc = 0; s->utf8_invalid = 0; @@ -229,18 +270,22 @@ std: return PHP_JSON_T_ERROR; } UTF16_1 { + PHP_JSON_TOKEN_LOCATION(last_column) += 1; s->str_esc += 5; PHP_JSON_CONDITION_GOTO(STR_P1); } UTF16_2 { + PHP_JSON_TOKEN_LOCATION(last_column) += 1; s->str_esc += 4; PHP_JSON_CONDITION_GOTO(STR_P1); } UTF16_3 { + PHP_JSON_TOKEN_LOCATION(last_column) += 1; s->str_esc += 3; PHP_JSON_CONDITION_GOTO(STR_P1); } UTF16_4 { + PHP_JSON_TOKEN_LOCATION(last_column) += 1; s->str_esc += 8; PHP_JSON_CONDITION_GOTO(STR_P1); } @@ -249,6 +294,7 @@ std: return PHP_JSON_T_ERROR; } ESC { + PHP_JSON_TOKEN_LOCATION(last_column) += 2; s->str_esc++; PHP_JSON_CONDITION_GOTO(STR_P1); } @@ -257,6 +303,7 @@ std: return PHP_JSON_T_ERROR; } ["] { + PHP_JSON_TOKEN_LOCATION(last_column)++; zend_string *str; size_t len = (size_t)(s->cursor - s->str_start - s->str_esc - 1 + s->utf8_invalid_count); if (len == 0) { @@ -277,7 +324,22 @@ std: return PHP_JSON_T_STRING; } } - UTF8 { PHP_JSON_CONDITION_GOTO(STR_P1); } + UTF8_1 { + PHP_JSON_TOKEN_LOCATION(last_column)++; + PHP_JSON_CONDITION_GOTO(STR_P1); + } + UTF8_2 { + PHP_JSON_TOKEN_LOCATION(last_column) += 1; + PHP_JSON_CONDITION_GOTO(STR_P1); + } + UTF8_3 { + PHP_JSON_TOKEN_LOCATION(last_column) += 1; + PHP_JSON_CONDITION_GOTO(STR_P1); + } + UTF8_4 { + PHP_JSON_TOKEN_LOCATION(last_column) += 1; + PHP_JSON_CONDITION_GOTO(STR_P1); + } ANY { if (s->options & (PHP_JSON_INVALID_UTF8_IGNORE | PHP_JSON_INVALID_UTF8_SUBSTITUTE)) { if (s->options & PHP_JSON_INVALID_UTF8_SUBSTITUTE) { @@ -295,7 +357,6 @@ std: s->errcode = PHP_JSON_ERROR_UTF8; return PHP_JSON_T_ERROR; } - UTF16_1 { int utf16 = php_json_ucs2_to_int(s, 2); PHP_JSON_SCANNER_COPY_UTF(); diff --git a/ext/json/php_json.h b/ext/json/php_json.h index b79c7c836f7a3..bbe8be9d60ada 100644 --- a/ext/json/php_json.h +++ b/ext/json/php_json.h @@ -86,6 +86,8 @@ ZEND_BEGIN_MODULE_GLOBALS(json) int encoder_depth; int encode_max_depth; php_json_error_code error_code; + size_t error_line; + size_t error_column; ZEND_END_MODULE_GLOBALS(json) PHP_JSON_API ZEND_EXTERN_MODULE_GLOBALS(json) diff --git a/ext/json/php_json_parser.h b/ext/json/php_json_parser.h index 8aedce9ac55d6..8fee3d11c6bf8 100644 --- a/ext/json/php_json_parser.h +++ b/ext/json/php_json_parser.h @@ -50,12 +50,20 @@ typedef struct _php_json_parser_methods { php_json_parser_func_object_end_t object_end; } php_json_parser_methods; + typedef struct _php_json_parser_location { + size_t first_line; + size_t first_column; + size_t last_line; + size_t last_column; +} php_json_parser_location; + struct _php_json_parser { php_json_scanner scanner; zval *return_value; int depth; int max_depth; php_json_parser_methods methods; + php_json_parser_location *location; }; PHP_JSON_API void php_json_parser_init_ex( @@ -77,6 +85,10 @@ PHP_JSON_API void php_json_parser_init( PHP_JSON_API php_json_error_code php_json_parser_error_code(const php_json_parser *parser); +PHP_JSON_API size_t php_json_parser_error_line(const php_json_parser *parser); + +PHP_JSON_API size_t php_json_parser_error_column(const php_json_parser *parser); + PHP_JSON_API int php_json_parse(php_json_parser *parser); int php_json_yyparse(php_json_parser *parser); diff --git a/ext/json/php_json_scanner.h b/ext/json/php_json_scanner.h index a49be68cd6328..a6de149391dc1 100644 --- a/ext/json/php_json_scanner.h +++ b/ext/json/php_json_scanner.h @@ -22,6 +22,17 @@ typedef unsigned char php_json_ctype; +typedef struct _php_json_error_location { + /** first column of the error */ + size_t first_column; + /** first line of the error */ + size_t first_line; + /** last column of the error */ + size_t last_column; + /** last line of the error */ + size_t last_line; +} php_json_error_location; + typedef struct _php_json_scanner { php_json_ctype *cursor; /* cursor position */ php_json_ctype *token; /* token position */ @@ -35,10 +46,12 @@ typedef struct _php_json_scanner { int state; /* condition state */ int options; /* options */ php_json_error_code errcode; /* error type if there is an error */ + php_json_error_location errloc; /* error location */ int utf8_invalid; /* whether utf8 is invalid */ int utf8_invalid_count; /* number of extra character for invalid utf8 */ } php_json_scanner; +#define PHP_JSON_SCANNER_LOCATION(scanner, slocation) (scanner).errloc.slocation void php_json_scanner_init(php_json_scanner *scanner, const char *str, size_t str_len, int options); int php_json_scan(php_json_scanner *s); diff --git a/ext/json/tests/007.phpt b/ext/json/tests/007.phpt index dea641317e97f..9a5ae654d6361 100644 --- a/ext/json/tests/007.phpt +++ b/ext/json/tests/007.phpt @@ -24,14 +24,14 @@ int(0) string(8) "No error" NULL int(1) -string(28) "Maximum stack depth exceeded" +string(46) "Maximum stack depth exceeded near location 1:2" NULL int(2) -string(42) "State mismatch (invalid or malformed JSON)" +string(60) "State mismatch (invalid or malformed JSON) near location 1:3" NULL int(3) -string(53) "Control character error, possibly incorrectly encoded" +string(71) "Control character error, possibly incorrectly encoded near location 1:2" NULL int(4) -string(12) "Syntax error" +string(30) "Syntax error near location 1:3" Done diff --git a/ext/json/tests/bug62010.phpt b/ext/json/tests/bug62010.phpt index 2591231dcdda1..862d7dc7e2c00 100644 --- a/ext/json/tests/bug62010.phpt +++ b/ext/json/tests/bug62010.phpt @@ -10,4 +10,4 @@ var_dump(json_last_error_msg()); --EXPECT-- NULL bool(true) -string(50) "Single unpaired UTF-16 surrogate in unicode escape" +string(68) "Single unpaired UTF-16 surrogate in unicode escape near location 1:1" diff --git a/ext/json/tests/bug68546.phpt b/ext/json/tests/bug68546.phpt index 8835a72c5eac7..1847eabf3a8e3 100644 --- a/ext/json/tests/bug68546.phpt +++ b/ext/json/tests/bug68546.phpt @@ -5,7 +5,7 @@ Bug #68546 (json_decode() Fatal error: Cannot access property started with '\0') var_dump(json_decode('{"key": {"\u0000": "aa"}}')); var_dump(json_last_error() === JSON_ERROR_INVALID_PROPERTY_NAME); -var_dump(json_decode('[{"key1": 0, "\u0000": 1}]')); +var_dump(json_decode('[{"key1": 0, "\u1234": 1, "\u0000": 1}]')); var_dump(json_last_error() === JSON_ERROR_INVALID_PROPERTY_NAME); var_dump(json_last_error_msg()); @@ -16,5 +16,5 @@ NULL bool(true) NULL bool(true) -string(36) "The decoded property name is invalid" +string(55) "The decoded property name is invalid near location 1:27" Done diff --git a/ext/json/tests/json_decode_exceptions.phpt b/ext/json/tests/json_decode_exceptions.phpt index 7dc2e7408a02c..d53941682e454 100644 --- a/ext/json/tests/json_decode_exceptions.phpt +++ b/ext/json/tests/json_decode_exceptions.phpt @@ -13,7 +13,7 @@ try { --EXPECTF-- object(JsonException)#1 (7) { ["message":protected]=> - string(12) "Syntax error" + string(30) "Syntax error near location 1:2" ["string":"Exception":private]=> string(0) "" ["code":protected]=> diff --git a/ext/json/tests/json_last_error_msg_error_location_001.phpt b/ext/json/tests/json_last_error_msg_error_location_001.phpt new file mode 100644 index 0000000000000..e0553f9f7d651 --- /dev/null +++ b/ext/json/tests/json_last_error_msg_error_location_001.phpt @@ -0,0 +1,121 @@ +--TEST-- +json_last_error_msg() - Error location reporting with ASCII characters +--FILE-- + +--EXPECT-- +Testing errors at various locations with ASCII characters + +Error at position 1:1: +bool(false) +int(4) +string(30) "Syntax error near location 1:1" + +Error at position 1:10: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:10" + +Error at position 1:9: +bool(false) +int(4) +string(30) "Syntax error near location 1:9" + +Error at position 1:16: +bool(false) +int(4) +string(31) "Syntax error near location 1:16" + +Error at position 1:15: +bool(false) +int(4) +string(31) "Syntax error near location 1:15" + +Error at position 1:10: +bool(false) +int(4) +string(31) "Syntax error near location 1:10" + +Error at position 1:7: +bool(false) +int(4) +string(30) "Syntax error near location 1:7" + +Error at position 1:2: +bool(false) +int(4) +string(30) "Syntax error near location 1:2" + +Error at position 1:16: +bool(false) +int(4) +string(31) "Syntax error near location 1:16" + +Error at position 1:4: +bool(false) +int(4) +string(30) "Syntax error near location 1:4" + +Error at position 1:10: +bool(false) +int(4) +string(31) "Syntax error near location 1:10" + +Error at position 1:10: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:10" + diff --git a/ext/json/tests/json_last_error_msg_error_location_002.phpt b/ext/json/tests/json_last_error_msg_error_location_002.phpt new file mode 100644 index 0000000000000..df7fc981ccbab --- /dev/null +++ b/ext/json/tests/json_last_error_msg_error_location_002.phpt @@ -0,0 +1,103 @@ +--TEST-- +json_last_error_msg() - Error location reporting with Unicode UTF-8 characters +--FILE-- + +--EXPECT-- +Testing error locations with Unicode UTF-8 characters + +Error after Japanese characters: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:12" + +Error after Russian characters: +bool(false) +int(3) +string(71) "Control character error, possibly incorrectly encoded near location 1:9" + +Error after Chinese characters: +bool(false) +int(3) +string(71) "Control character error, possibly incorrectly encoded near location 1:8" + +Error after Arabic characters: +bool(false) +int(3) +string(71) "Control character error, possibly incorrectly encoded near location 1:9" + +Error after Emoji: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:11" + +Error in mixed ASCII and UTF-8: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:27" + +Error with UTF-8 escaped sequences: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:10" + +Error in object with multiple UTF-8 keys: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:22" + +Error in array with UTF-8 strings: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:18" + +Error in nested object with UTF-8: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:15" + diff --git a/ext/json/tests/json_last_error_msg_error_location_003.phpt b/ext/json/tests/json_last_error_msg_error_location_003.phpt new file mode 100644 index 0000000000000..ec5e6b9b4d651 --- /dev/null +++ b/ext/json/tests/json_last_error_msg_error_location_003.phpt @@ -0,0 +1,72 @@ +--TEST-- +json_last_error_msg() - Error location reporting with multi-line JSON +--FILE-- + +--EXPECT-- +Testing error locations in multi-line JSON + +Error on line 2, column 13: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 2:13" + +Error on line 3, column 12: +bool(false) +int(4) +string(31) "Syntax error near location 3:12" + +Error on line 5, column 26: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 5:26" + +Error on line 7, column 1: +bool(false) +int(4) +string(30) "Syntax error near location 7:1" + diff --git a/ext/json/tests/json_last_error_msg_error_location_004.phpt b/ext/json/tests/json_last_error_msg_error_location_004.phpt new file mode 100644 index 0000000000000..165449600fb39 --- /dev/null +++ b/ext/json/tests/json_last_error_msg_error_location_004.phpt @@ -0,0 +1,93 @@ +--TEST-- +json_last_error_msg() - Error location reporting with deeply nested structures +--FILE-- + +--EXPECT-- +Testing error locations in deeply nested structures + +Error in deeply nested object: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:31" + +Error in deeply nested array: +bool(true) +int(0) +string(8) "No error" + +Error in mixed nested structures: +bool(true) +int(0) +string(8) "No error" + +Error at end of deep nesting: +bool(true) +int(0) +string(8) "No error" + +Error in middle of deep nesting: +bool(false) +int(4) +string(31) "Syntax error near location 1:21" + +Error in complex structure: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:93" + +Error in array of objects: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:68" + +Error in object with array values: +bool(false) +int(2) +string(61) "State mismatch (invalid or malformed JSON) near location 1:82" + diff --git a/ext/json/tests/json_last_error_msg_error_location_005.phpt b/ext/json/tests/json_last_error_msg_error_location_005.phpt new file mode 100644 index 0000000000000..d12ce387e73ea --- /dev/null +++ b/ext/json/tests/json_last_error_msg_error_location_005.phpt @@ -0,0 +1,103 @@ +--TEST-- +json_last_error_msg() - Error location reporting with UTF-16 surrogate pairs +--FILE-- + +--EXPECT-- +Testing error locations with UTF-16 surrogate pairs and escape sequences + +Error after UTF-16 escaped emoji: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:11" + +Error after multiple UTF-16 pairs: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:10" + +Error with mixed UTF-8 and UTF-16: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:11" + +Error with UTF-16 in key: +bool(false) +int(3) +string(71) "Control character error, possibly incorrectly encoded near location 1:9" + +Error with multiple UTF-16 keys: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:22" + +Error with BMP characters: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:10" + +Error with supplementary plane: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:11" + +Error in array with UTF-16: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:12" + +Error in nested structure with UTF-16: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:18" + +Error with UTF-16 and control chars: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:10" + diff --git a/ext/json/tests/json_last_error_msg_error_location_006.phpt b/ext/json/tests/json_last_error_msg_error_location_006.phpt new file mode 100644 index 0000000000000..e6aab1af8f27e --- /dev/null +++ b/ext/json/tests/json_last_error_msg_error_location_006.phpt @@ -0,0 +1,152 @@ +--TEST-- +json_last_error_msg() - Error location reporting edge cases +--FILE-- + +--EXPECT-- +Testing error location edge cases + +Error at position 1:1: +bool(false) +int(4) +string(12) "Syntax error" + +Error at position 1:1 with invalid char: +bool(false) +int(4) +string(30) "Syntax error near location 1:1" + +Error after leading whitespace: +bool(false) +int(4) +string(30) "Syntax error near location 1:5" + +Error with tabs and spaces: +bool(false) +int(4) +string(30) "Syntax error near location 2:3" + +Error after multiple newlines: +bool(false) +int(4) +string(30) "Syntax error near location 4:2" + +Error at end of long string: +bool(false) +int(4) +string(33) "Syntax error near location 1:1011" + +Error with very long key: +bool(false) +int(3) +string(73) "Control character error, possibly incorrectly encoded near location 1:506" + +Error after empty object: +bool(false) +int(4) +string(30) "Syntax error near location 1:3" + +Error after empty array: +bool(false) +int(4) +string(30) "Syntax error near location 1:3" + +Error with multiple root values: +bool(false) +int(4) +string(30) "Syntax error near location 1:3" + +Error after valid number: +bool(false) +int(4) +string(30) "Syntax error near location 1:4" + +Error after valid boolean: +bool(false) +int(4) +string(30) "Syntax error near location 1:5" + +Error after valid null: +bool(false) +int(4) +string(30) "Syntax error near location 1:5" + +Error after valid string: +bool(false) +int(4) +string(30) "Syntax error near location 1:7" + +Error with mixed whitespace: +bool(false) +int(3) +string(71) "Control character error, possibly incorrectly encoded near location 3:2" + diff --git a/ext/json/tests/json_last_error_msg_error_location_007.phpt b/ext/json/tests/json_last_error_msg_error_location_007.phpt new file mode 100644 index 0000000000000..0e24889bbbbeb --- /dev/null +++ b/ext/json/tests/json_last_error_msg_error_location_007.phpt @@ -0,0 +1,178 @@ +--TEST-- +json_last_error_msg() - Error location with various error types +--FILE-- + +--EXPECT-- +Testing error locations with different error types + +State mismatch - expected value: +bool(false) +int(4) +string(30) "Syntax error near location 1:9" + +State mismatch - expected key: +bool(false) +int(4) +string(30) "Syntax error near location 1:2" + +State mismatch - trailing comma in object: +bool(false) +int(4) +string(31) "Syntax error near location 1:17" + +State mismatch - trailing comma in array: +bool(false) +int(4) +string(31) "Syntax error near location 1:10" + +Invalid number format - leading zero: +bool(false) +int(4) +string(31) "Syntax error near location 1:10" + +Invalid number format - multiple decimals: +bool(false) +int(4) +string(31) "Syntax error near location 1:12" + +Invalid number format - incomplete exponent: +bool(false) +int(4) +string(31) "Syntax error near location 1:10" + +Invalid number format - double sign: +bool(false) +int(4) +string(30) "Syntax error near location 1:9" + +Unclosed string: +bool(false) +int(3) +string(71) "Control character error, possibly incorrectly encoded near location 1:9" + +Invalid escape sequence: +bool(false) +int(4) +string(30) "Syntax error near location 1:9" + +Incomplete unicode escape: +bool(false) +int(4) +string(30) "Syntax error near location 1:9" + +Invalid unicode escape: +bool(false) +int(4) +string(30) "Syntax error near location 1:9" + +Invalid true keyword: +bool(false) +int(4) +string(30) "Syntax error near location 1:9" + +Invalid false keyword: +bool(false) +int(4) +string(30) "Syntax error near location 1:9" + +Invalid null keyword: +bool(false) +int(4) +string(30) "Syntax error near location 1:9" + +Mismatched brackets - ] instead of }: +bool(false) +int(2) +string(61) "State mismatch (invalid or malformed JSON) near location 1:14" + +Mismatched brackets - } instead of ]: +bool(false) +int(2) +string(60) "State mismatch (invalid or malformed JSON) near location 1:7" + +Extra closing bracket: +bool(false) +int(4) +string(31) "Syntax error near location 1:15" + +Missing comma between elements: +bool(false) +int(4) +string(30) "Syntax error near location 1:4" + +Missing comma between object properties: +bool(false) +int(4) +string(30) "Syntax error near location 1:9" + diff --git a/ext/json/tests/json_last_error_msg_error_location_008.phpt b/ext/json/tests/json_last_error_msg_error_location_008.phpt new file mode 100644 index 0000000000000..4d8a1012316b4 --- /dev/null +++ b/ext/json/tests/json_last_error_msg_error_location_008.phpt @@ -0,0 +1,182 @@ +--TEST-- +json_last_error_msg() - Error location with mixed UTF-8 multi-byte characters +--FILE-- + +--EXPECT-- +Testing error locations with various UTF-8 multi-byte character widths + +Error with 2-byte UTF-8 (Latin Extended): +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:10" + +Error with 2-byte UTF-8 (Greek): +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:14" + +Error with 2-byte UTF-8 (Cyrillic): +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:12" + +Error with 3-byte UTF-8 (Chinese): +bool(false) +int(3) +string(71) "Control character error, possibly incorrectly encoded near location 1:8" + +Error with 3-byte UTF-8 (Japanese Hiragana): +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:10" + +Error with 3-byte UTF-8 (Japanese Katakana): +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:10" + +Error with 3-byte UTF-8 (Korean): +bool(false) +int(3) +string(71) "Control character error, possibly incorrectly encoded near location 1:8" + +Error with 4-byte UTF-8 (Emoji faces): +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:11" + +Error with 4-byte UTF-8 (Emoji objects): +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:13" + +Error with 4-byte UTF-8 (Mathematical symbols): +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:10" + +Error with mixed 1-2-3 byte UTF-8: +bool(false) +int(3) +string(71) "Control character error, possibly incorrectly encoded near location 1:9" + +Error with mixed 2-3-4 byte UTF-8: +bool(false) +int(3) +string(71) "Control character error, possibly incorrectly encoded near location 1:9" + +Error with all byte widths: +bool(false) +int(3) +string(71) "Control character error, possibly incorrectly encoded near location 1:9" + +Error with UTF-8 key at start: +bool(false) +int(3) +string(71) "Control character error, possibly incorrectly encoded near location 1:7" + +Error with multiple UTF-8 keys: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:35" + +Error in array with mixed UTF-8: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:25" + +Error in nested structure with various UTF-8: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:22" + +Error with combining diacritical marks: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:10" + +Error with Hebrew: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:10" + +Error with Arabic with diacritics: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:11" + diff --git a/ext/json/tests/json_last_error_msg_error_location_009.phpt b/ext/json/tests/json_last_error_msg_error_location_009.phpt new file mode 100644 index 0000000000000..406179693ef69 --- /dev/null +++ b/ext/json/tests/json_last_error_msg_error_location_009.phpt @@ -0,0 +1,110 @@ +--TEST-- +json_last_error_msg() - Error location with depth errors and complex nesting +--FILE-- + +--EXPECT-- +Testing error locations with depth-related issues + +Max depth error at specific location: +bool(false) +int(1) +string(47) "Maximum stack depth exceeded near location 1:21" + +Max depth error in array: +bool(false) +int(1) +string(46) "Maximum stack depth exceeded near location 1:5" + +Max depth error with mixed structures: +bool(false) +int(1) +string(46) "Maximum stack depth exceeded near location 1:7" + +Syntax error at deep nesting level: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:31" + +Syntax error in deep array: +bool(false) +int(3) +string(71) "Control character error, possibly incorrectly encoded near location 1:6" + +Error after valid deep structure: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:48" + +Error in middle of nested structure: +bool(false) +int(4) +string(31) "Syntax error near location 1:29" + +Error in array with nested objects: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:30" + +Error in deep UTF-8 structure: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:16" + +Valid deep structure within limit: +bool(true) +int(0) +string(8) "No error" + diff --git a/ext/json/tests/json_last_error_msg_error_location_010.phpt b/ext/json/tests/json_last_error_msg_error_location_010.phpt new file mode 100644 index 0000000000000..1085702058387 --- /dev/null +++ b/ext/json/tests/json_last_error_msg_error_location_010.phpt @@ -0,0 +1,164 @@ +--TEST-- +json_last_error_msg() - Error location with whitespace variations +--FILE-- + +--EXPECT-- +Testing error locations with various whitespace patterns + +Error after multiple spaces: +bool(false) +int(4) +string(30) "Syntax error near location 1:7" + +Error after tabs: +bool(false) +int(4) +string(30) "Syntax error near location 1:5" + +Error after mixed whitespace: +bool(false) +int(4) +string(30) "Syntax error near location 1:7" + +Error on second line: +bool(false) +int(4) +string(30) "Syntax error near location 2:2" + +Error with CRLF line endings: +bool(false) +int(3) +string(71) "Control character error, possibly incorrectly encoded near location 2:8" + +Error after blank lines: +bool(false) +int(4) +string(30) "Syntax error near location 4:2" + +Error in string with spaces: +bool(false) +int(3) +string(71) "Control character error, possibly incorrectly encoded near location 1:9" + +Error with whitespace around colon: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:12" + +Error with whitespace around comma: +bool(true) +int(0) +string(8) "No error" + +Error in pretty printed JSON: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 4:11" + +Error in heavily indented JSON: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 5:26" + +Error with mixed tabs and spaces: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 3:12" + +Error after whitespace-only line: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 3:12" + +Error in compact JSON: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:22" + +Error with regular spaces: +bool(false) +int(3) +string(72) "Control character error, possibly incorrectly encoded near location 1:10" + diff --git a/ext/json/tests/json_validate_002.phpt b/ext/json/tests/json_validate_002.phpt index 53f4e4f2c2ea1..423564c4ad7c4 100644 --- a/ext/json/tests/json_validate_002.phpt +++ b/ext/json/tests/json_validate_002.phpt @@ -23,13 +23,13 @@ int(4) string(12) "Syntax error" bool(false) int(4) -string(12) "Syntax error" +string(30) "Syntax error near location 1:1" bool(false) int(4) string(12) "Syntax error" bool(false) int(1) -string(28) "Maximum stack depth exceeded" +string(46) "Maximum stack depth exceeded near location 1:1" bool(true) int(0) string(8) "No error" @@ -44,7 +44,7 @@ int(0) string(8) "No error" bool(false) int(4) -string(12) "Syntax error" +string(30) "Syntax error near location 1:1" bool(true) int(0) string(8) "No error" diff --git a/ext/json/tests/json_validate_004.phpt b/ext/json/tests/json_validate_004.phpt index d8a798d943278..bd807defa1404 100644 --- a/ext/json/tests/json_validate_004.phpt +++ b/ext/json/tests/json_validate_004.phpt @@ -23,16 +23,16 @@ json_validate_trycatchdump("[\"\xc1\xc1\",\"a\"]", 512, JSON_INVALID_UTF8_IGNORE Testing Invalid UTF-8 bool(false) int(5) -string(56) "Malformed UTF-8 characters, possibly incorrectly encoded" +string(74) "Malformed UTF-8 characters, possibly incorrectly encoded near location 1:1" bool(false) int(5) -string(56) "Malformed UTF-8 characters, possibly incorrectly encoded" +string(74) "Malformed UTF-8 characters, possibly incorrectly encoded near location 1:1" bool(false) int(5) -string(56) "Malformed UTF-8 characters, possibly incorrectly encoded" +string(74) "Malformed UTF-8 characters, possibly incorrectly encoded near location 1:1" bool(false) int(5) -string(56) "Malformed UTF-8 characters, possibly incorrectly encoded" +string(74) "Malformed UTF-8 characters, possibly incorrectly encoded near location 1:2" bool(true) int(0) string(8) "No error"