Skip to content

better json error message #14672

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 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
32 changes: 30 additions & 2 deletions ext/json/json.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,17 @@ static PHP_GINIT_FUNCTION(json)
static PHP_RINIT_FUNCTION(json)
{
JSON_G(error_code) = 0;
JSON_G(error_msg) = NULL;

return SUCCESS;
}

static PHP_RSHUTDOWN_FUNCTION(json)
{
if (JSON_G(error_msg)) {
efree(JSON_G(error_msg));
}

return SUCCESS;
}

Expand Down Expand Up @@ -209,6 +220,8 @@ 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);
JSON_G(error_code) = error_code;
php_json_ctype* error_msg = php_json_parser_error_msg(&parser);
JSON_G(error_msg) = error_msg;
return false;
}

Expand Down Expand Up @@ -359,11 +372,26 @@ PHP_FUNCTION(json_last_error)
}
/* }}} */

/* {{{ Returns the error string of the last json_encode() or json_decode() call. */
/* {{{ Returns the error string of the last json_validate(), json_encode() or json_decode() call. */
PHP_FUNCTION(json_last_error_msg)
{
ZEND_PARSE_PARAMETERS_NONE();

RETURN_STRING(php_json_get_error_msg(JSON_G(error_code)));
const char* msg = php_json_get_error_msg(JSON_G(error_code));

if (JSON_G(error_code) == PHP_JSON_ERROR_NONE) {
RETURN_STRING(msg);
}

char* msg_combined;

if (JSON_G(error_msg) && strlen((const char*) JSON_G(error_msg)) > 0) {
spprintf(&msg_combined, 0, "%s - %s", msg, (const char*) JSON_G(error_msg));
RETVAL_STRING(msg_combined);
efree(msg_combined);
return;
} else {
RETURN_STRING(msg);
}
}
/* }}} */
24 changes: 24 additions & 0 deletions ext/json/json_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "php.h"
#include "php_json.h"
#include "php_json_parser.h"
#include "php_json_scanner.h"

#define YYDEBUG 0

Expand Down Expand Up @@ -301,13 +302,36 @@ static void php_json_yyerror(php_json_parser *parser, char const *msg)
if (!parser->scanner.errcode) {
parser->scanner.errcode = PHP_JSON_ERROR_SYNTAX;
}

char error_message[256];
snprintf(
error_message,
sizeof(error_message),
"error: %s, at character %ld near content: %s",
msg,
parser->scanner.character_count,
parser->scanner.token
);

if (parser->scanner.error_msg) {
free(parser->scanner.error_msg);
}

parser->scanner.error_msg = (php_json_ctype *) malloc(strlen(error_message) + 1);

memcpy(parser->scanner.error_msg, error_message, strlen(error_message) + 1);
}

PHP_JSON_API php_json_error_code php_json_parser_error_code(const php_json_parser *parser)
{
return parser->scanner.errcode;
}

PHP_JSON_API php_json_ctype* php_json_parser_error_msg(const php_json_parser *parser)
{
return parser->scanner.error_msg;
}

static const php_json_parser_methods default_parser_methods =
{
php_json_parser_array_create,
Expand Down
8 changes: 8 additions & 0 deletions ext/json/json_scanner.re
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,24 @@ 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;
s->character_count = 0;
PHP_JSON_CONDITION_SET(JS);
}

int php_json_scan(php_json_scanner *s)
{
ZVAL_NULL(&s->value);
static unsigned long int token_total_length = 0;

std:
s->token = s->cursor;

if (token_total_length == 0) {
token_total_length = strlen((const char*) s->token);
} else {
s->character_count = token_total_length + (-1 * strlen((const char*) s->token));
}

/*!re2c
re2c:indent:top = 1;
re2c:yyfill:enable = 0;
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;
unsigned char *error_msg;
ZEND_END_MODULE_GLOBALS(json)

PHP_JSON_API ZEND_EXTERN_MODULE_GLOBALS(json)
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 php_json_ctype* php_json_parser_error_msg(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
2 changes: 2 additions & 0 deletions ext/json/php_json_scanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@ typedef struct _php_json_scanner {
php_json_ctype *ctxmarker; /* marker position for context backtracking */
php_json_ctype *str_start; /* start position of the string */
php_json_ctype *pstr; /* string pointer for escapes conversion */
php_json_ctype *error_msg; /* error message if there is an error */
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 */
int utf8_invalid; /* whether utf8 is invalid */
int utf8_invalid_count; /* number of extra character for invalid utf8 */
size_t character_count; /* counts the number of characters scanned (not tokens) */
} php_json_scanner;


Expand Down
2 changes: 1 addition & 1 deletion ext/json/tests/json_validate_001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var_dump(
json_validate('{ "": { "foo": "" } }'),
json_validate('{ "": { "": "" } }'),
json_validate('{ "test": {"foo": "bar"}, "test2": {"foo" : "bar" }, "test2": {"foo" : "bar" } }'),
json_validate('{ "test": {"foo": "bar"}, "test2": {"foo" : "bar" }, "test3": {"foo" : "bar" } }'),
json_validate('{ "test": {"foo": "bar"}, "test2": {"foo" : "bar" }, "test3": {"foo" : "bar" } }')
);

?>
Expand Down
6 changes: 3 additions & 3 deletions ext/json/tests/json_validate_002.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ int(4)
string(12) "Syntax error"
bool(false)
int(4)
string(12) "Syntax error"
string(66) "Syntax error - error: syntax error, at character 0 near content: -"
bool(false)
int(4)
string(12) "Syntax error"
string(66) "Syntax error - error: syntax error, at character 0 near content: -"
bool(false)
int(1)
string(28) "Maximum stack depth exceeded"
Expand All @@ -44,7 +44,7 @@ int(0)
string(8) "No error"
bool(false)
int(4)
string(12) "Syntax error"
string(66) "Syntax error - error: syntax error, at character 0 near content: -"
bool(true)
int(0)
string(8) "No error"
10 changes: 5 additions & 5 deletions ext/json/tests/json_validate_004.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,20 @@ json_validate_trycatchdump("\"\x61\xf0\x80\x80\x41\"", 512, JSON_INVALID_UTF8_IG
json_validate_trycatchdump("[\"\xc1\xc1\",\"a\"]", 512, JSON_INVALID_UTF8_IGNORE);

?>
--EXPECT--
--EXPECTF--
Testing Invalid UTF-8
bool(false)
int(5)
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
string(114) "Malformed UTF-8 characters, possibly incorrectly encoded - error: syntax error, at character 0 near content: %s"
bool(false)
int(5)
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
string(116) "Malformed UTF-8 characters, possibly incorrectly encoded - error: syntax error, at character -1 near content: %s"
bool(false)
int(5)
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
string(117) "Malformed UTF-8 characters, possibly incorrectly encoded - error: syntax error, at character -2 near content: %s"
bool(false)
int(5)
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
string(119) "Malformed UTF-8 characters, possibly incorrectly encoded - error: syntax error, at character -4 near content: %s"
bool(true)
int(0)
string(8) "No error"
Expand Down
Loading