Skip to content

json_last_error_msg - better message - error position near by #18866

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion ext/json/json.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ static PHP_GINIT_FUNCTION(json)
static PHP_RINIT_FUNCTION(json)
{
JSON_G(error_code) = 0;
JSON_G(error_pos) = 0;
return SUCCESS;
}

Expand Down Expand Up @@ -133,6 +134,7 @@ PHP_JSON_API zend_result php_json_encode_ex(smart_str *buf, zval *val, int optio

return_code = php_json_encode_zval(buf, val, options, &encoder);
JSON_G(error_code) = encoder.error_code;
JSON_G(error_pos) = encoder.error_pos;

return return_code;
}
Expand Down Expand Up @@ -177,6 +179,29 @@ static const char *php_json_get_error_msg(php_json_error_code error_code) /* {{{
}
/* }}} */


char *php_json_get_error_msg_wrapper(php_json_error_code error_code) /* {{{ */
{
char *final_message;
const char *error_message = php_json_get_error_msg(error_code);

switch(error_code) {
case PHP_JSON_ERROR_SYNTAX:
case PHP_JSON_ERROR_UTF8:
case PHP_JSON_ERROR_CTRL_CHAR:
case PHP_JSON_ERROR_UNSUPPORTED_TYPE:
case PHP_JSON_ERROR_INVALID_PROPERTY_NAME:
case PHP_JSON_ERROR_UTF16:
spprintf(&final_message, 0, "%s near character %zu", error_message, JSON_G(error_pos));
break;
default:
spprintf(&final_message, 0, "%s", error_message);
}

return final_message;
}
/* }}} */

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;
Expand All @@ -185,11 +210,15 @@ 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_pos = php_json_parser_error_pos(&parser);

if (!(options & PHP_JSON_THROW_ON_ERROR)) {
JSON_G(error_code) = error_code;
JSON_G(error_pos) = error_pos;
} else {
zend_throw_exception(php_json_exception_ce, php_json_get_error_msg(error_code), error_code);
}

RETVAL_NULL();
return FAILURE;
}
Expand All @@ -208,7 +237,9 @@ 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_pos = php_json_parser_error_pos(&parser);
JSON_G(error_code) = error_code;
JSON_G(error_pos) = error_pos;
return false;
}

Expand Down Expand Up @@ -238,6 +269,7 @@ PHP_FUNCTION(json_encode)

if (!(options & PHP_JSON_THROW_ON_ERROR) || (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
JSON_G(error_code) = encoder.error_code;
JSON_G(error_pos) = encoder.error_pos;
if (encoder.error_code != PHP_JSON_ERROR_NONE && !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
smart_str_free(&buf);
RETURN_FALSE;
Expand Down Expand Up @@ -364,6 +396,8 @@ PHP_FUNCTION(json_last_error_msg)
{
ZEND_PARSE_PARAMETERS_NONE();

RETURN_STRING(php_json_get_error_msg(JSON_G(error_code)));
char *msg = php_json_get_error_msg_wrapper(JSON_G(error_code));
RETVAL_STRING(msg);
efree(msg);
}
/* }}} */
12 changes: 11 additions & 1 deletion ext/json/json_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ int json_yydebug = 1;
#define PHP_JSON_DEPTH_INC \
if (parser->max_depth && parser->depth >= parser->max_depth) { \
parser->scanner.errcode = PHP_JSON_ERROR_DEPTH; \
parser->scanner.errpos = (size_t)((parser->scanner.str_start - parser->scanner.input_start) - parser->scanner.str_esc - parser->scanner.utf8_invalid_count); \
YYERROR; \
} \
++parser->depth
Expand Down Expand Up @@ -108,6 +109,7 @@ object_end:
| ']'
{
parser->scanner.errcode = PHP_JSON_ERROR_STATE_MISMATCH;
parser->scanner.errpos = (size_t)((parser->scanner.str_start - parser->scanner.input_start) - parser->scanner.str_esc - parser->scanner.utf8_invalid_count); \
YYERROR;
}
;
Expand Down Expand Up @@ -164,6 +166,7 @@ array_end:
| '}'
{
parser->scanner.errcode = PHP_JSON_ERROR_STATE_MISMATCH;
parser->scanner.errpos = (size_t)((parser->scanner.str_start - parser->scanner.input_start) - parser->scanner.str_esc - parser->scanner.utf8_invalid_count); \
YYERROR;
}
;
Expand Down Expand Up @@ -242,6 +245,7 @@ static int php_json_parser_object_update(php_json_parser *parser, zval *object,
} else {
if (ZSTR_LEN(key) > 0 && ZSTR_VAL(key)[0] == '\0') {
parser->scanner.errcode = PHP_JSON_ERROR_INVALID_PROPERTY_NAME;
parser->scanner.errpos = (size_t)((parser->scanner.str_start - parser->scanner.input_start) - parser->scanner.str_esc - parser->scanner.utf8_invalid_count); \
zend_string_release_ex(key, 0);
zval_ptr_dtor_nogc(zvalue);
zval_ptr_dtor_nogc(object);
Expand Down Expand Up @@ -300,6 +304,7 @@ static void php_json_yyerror(php_json_parser *parser, char const *msg)
{
if (!parser->scanner.errcode) {
parser->scanner.errcode = PHP_JSON_ERROR_SYNTAX;
parser->scanner.errpos = (size_t)((parser->scanner.str_start - parser->scanner.input_start) - parser->scanner.str_esc - parser->scanner.utf8_invalid_count);
}
}

Expand All @@ -308,6 +313,11 @@ 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_pos(const php_json_parser *parser)
{
return parser->scanner.errpos;
}

static const php_json_parser_methods default_parser_methods =
{
php_json_parser_array_create,
Expand Down Expand Up @@ -339,7 +349,7 @@ PHP_JSON_API void php_json_parser_init_ex(php_json_parser *parser,
int options,
int max_depth,
const php_json_parser_methods *parser_methods)
{
{
memset(parser, 0, sizeof(php_json_parser));
php_json_scanner_init(&parser->scanner, str, str_len, options);
parser->depth = 1;
Expand Down
29 changes: 29 additions & 0 deletions ext/json/json_scanner.re
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,24 @@ static int php_json_ucs2_to_int(php_json_scanner *s, int size)

void php_json_scanner_init(php_json_scanner *s, const char *str, size_t str_len, int options)
{
s->token = NULL;
s->marker = NULL;
s->ctxmarker = NULL;
s->pstr = NULL;
s->str_esc = 0;
s->state = 0;
s->errcode = PHP_JSON_ERROR_NONE;
s->errpos = 0; /* Initialize errpos */
s->utf8_invalid = 0;
s->utf8_invalid_count = 0;
s->str_start = (php_json_ctype *)str; /* Initialize str_start */
s->input_start = (php_json_ctype *)str; /* Initialize input_start */

s->cursor = (php_json_ctype *) str;
s->limit = (php_json_ctype *) str + str_len;
s->options = options;
s->errpos = 0;
s->errcode = 0;
PHP_JSON_CONDITION_SET(JS);
}

Expand Down Expand Up @@ -201,6 +216,7 @@ std:
return PHP_JSON_T_EOI;
} else {
s->errcode = PHP_JSON_ERROR_CTRL_CHAR;
s->errpos = (size_t)(s->str_start - s->input_start - s->str_esc - s->utf8_invalid_count);
return PHP_JSON_T_ERROR;
}
}
Expand All @@ -213,19 +229,23 @@ std:
}
<JS>CTRL {
s->errcode = PHP_JSON_ERROR_CTRL_CHAR;
s->errpos = (size_t)(s->str_start - s->input_start - s->str_esc - s->utf8_invalid_count);
return PHP_JSON_T_ERROR;
}
<JS>UTF8 {
s->errcode = PHP_JSON_ERROR_SYNTAX;
s->errpos = (size_t)(s->str_start - s->input_start - s->str_esc - s->utf8_invalid_count);
return PHP_JSON_T_ERROR;
}
<JS>ANY {
s->errcode = PHP_JSON_ERROR_UTF8;
s->errpos = (size_t)(s->str_start - s->input_start - s->str_esc - s->utf8_invalid_count);
return PHP_JSON_T_ERROR;
}

<STR_P1>CTRL {
s->errcode = PHP_JSON_ERROR_CTRL_CHAR;
s->errpos = (size_t)(s->str_start - s->input_start - s->str_esc - s->utf8_invalid_count);
return PHP_JSON_T_ERROR;
}
<STR_P1>UTF16_1 {
Expand All @@ -246,6 +266,7 @@ std:
}
<STR_P1>UCS2 {
s->errcode = PHP_JSON_ERROR_UTF16;
s->errpos = (size_t)(s->str_start - s->input_start - s->str_esc - s->utf8_invalid_count);
return PHP_JSON_T_ERROR;
}
<STR_P1>ESC {
Expand All @@ -254,6 +275,8 @@ std:
}
<STR_P1>ESCPREF {
s->errcode = PHP_JSON_ERROR_SYNTAX;
s->errpos = (size_t)(s->str_start - s->input_start - s->str_esc - s->utf8_invalid_count);

return PHP_JSON_T_ERROR;
}
<STR_P1>["] {
Expand Down Expand Up @@ -283,6 +306,7 @@ std:
if (s->options & PHP_JSON_INVALID_UTF8_SUBSTITUTE) {
if (s->utf8_invalid_count > INT_MAX - 2) {
s->errcode = PHP_JSON_ERROR_UTF8;
s->errpos = (size_t)(s->str_start - s->input_start - s->str_esc - s->utf8_invalid_count);
return PHP_JSON_T_ERROR;
}
s->utf8_invalid_count += 2;
Expand All @@ -293,6 +317,7 @@ std:
PHP_JSON_CONDITION_GOTO(STR_P1);
}
s->errcode = PHP_JSON_ERROR_UTF8;
s->errpos = (size_t)(s->str_start - s->input_start - s->str_esc - s->utf8_invalid_count);
return PHP_JSON_T_ERROR;
}

Expand Down Expand Up @@ -358,6 +383,8 @@ std:
break;
default:
s->errcode = PHP_JSON_ERROR_SYNTAX;
s->errpos = (size_t)(s->str_start - s->input_start - s->str_esc - s->utf8_invalid_count);

return PHP_JSON_T_ERROR;
}
*(s->pstr++) = esc;
Expand Down Expand Up @@ -386,6 +413,8 @@ std:

<*>ANY {
s->errcode = PHP_JSON_ERROR_SYNTAX;
s->errpos = (size_t)(s->str_start - s->input_start - s->str_esc - s->utf8_invalid_count);

return PHP_JSON_T_ERROR;
}
*/
Expand Down
1 change: 1 addition & 0 deletions ext/json/php_json.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ ZEND_BEGIN_MODULE_GLOBALS(json)
int encoder_depth;
int encode_max_depth;
php_json_error_code error_code;
size_t error_pos;
ZEND_END_MODULE_GLOBALS(json)

PHP_JSON_API ZEND_EXTERN_MODULE_GLOBALS(json)
Expand Down
1 change: 1 addition & 0 deletions ext/json/php_json_encoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct _php_json_encoder {
int depth;
int max_depth;
php_json_error_code error_code;
size_t error_pos;
};

static inline void php_json_encode_init(php_json_encoder *encoder)
Expand Down
2 changes: 2 additions & 0 deletions ext/json/php_json_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ 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_pos(const php_json_parser *parser);

PHP_JSON_API int php_json_parse(php_json_parser *parser);

int php_json_yyparse(php_json_parser *parser);
Expand Down
4 changes: 3 additions & 1 deletion ext/json/php_json_scanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ typedef struct _php_json_scanner {
php_json_ctype *limit; /* the last read character + 1 position */
php_json_ctype *marker; /* marker position for backtracking */
php_json_ctype *ctxmarker; /* marker position for context backtracking */
php_json_ctype *str_start; /* start position of the string */
php_json_ctype *str_start; /* dynamic position of the string under analysis */
php_json_ctype *input_start; /* fixed start position of the original string provided to the scanner */
php_json_ctype *pstr; /* string pointer for escapes conversion */
zval value; /* value */
int str_esc; /* number of extra characters for escaping */
int state; /* condition state */
int options; /* options */
php_json_error_code errcode; /* error type if there is an error */
size_t errpos; /* holds the aprox character number when an error occurs */
int utf8_invalid; /* whether utf8 is invalid */
int utf8_invalid_count; /* number of extra character for invalid utf8 */
} php_json_scanner;
Expand Down
4 changes: 2 additions & 2 deletions ext/json/tests/007.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ int(2)
string(42) "State mismatch (invalid or malformed JSON)"
NULL
int(3)
string(53) "Control character error, possibly incorrectly encoded"
string(70) "Control character error, possibly incorrectly encoded near character 2"
NULL
int(4)
string(12) "Syntax error"
string(29) "Syntax error near character 0"
Done
8 changes: 4 additions & 4 deletions ext/json/tests/bug54058.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ var_dump(json_last_error(), json_last_error_msg());
?>
--EXPECT--
int(5)
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
string(73) "Malformed UTF-8 characters, possibly incorrectly encoded near character 0"
int(5)
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
string(73) "Malformed UTF-8 characters, possibly incorrectly encoded near character 0"
int(5)
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
string(73) "Malformed UTF-8 characters, possibly incorrectly encoded near character 0"
int(5)
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
string(73) "Malformed UTF-8 characters, possibly incorrectly encoded near character 0"
8 changes: 4 additions & 4 deletions ext/json/tests/bug61537.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ var_dump(json_last_error(), json_last_error_msg());
--EXPECT--
bool(false)
int(5)
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
string(73) "Malformed UTF-8 characters, possibly incorrectly encoded near character 0"
string(4) "null"
int(5)
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
string(73) "Malformed UTF-8 characters, possibly incorrectly encoded near character 0"

bool(false)
int(5)
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
string(73) "Malformed UTF-8 characters, possibly incorrectly encoded near character 0"
string(4) "null"
int(5)
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
string(73) "Malformed UTF-8 characters, possibly incorrectly encoded near character 0"
2 changes: 1 addition & 1 deletion ext/json/tests/bug62010.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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(67) "Single unpaired UTF-16 surrogate in unicode escape near character 1"
2 changes: 1 addition & 1 deletion ext/json/tests/bug68546.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ NULL
bool(true)
NULL
bool(true)
string(36) "The decoded property name is invalid"
string(54) "The decoded property name is invalid near character 15"
Done
2 changes: 1 addition & 1 deletion ext/json/tests/json_encode_exceptions.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ object(JsonException)#1 (7) {
}
string(4) "null"
int(5)
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
string(73) "Malformed UTF-8 characters, possibly incorrectly encoded near character 0"
Loading