Skip to content

Commit 4662151

Browse files
committed
Improve JSON error handling
json_encode() no longer throws warnings. Instead only the error code for json_last_error() is set. As it is hard to debug the error from just an error code an optional $as_string parameter was added to json_last_error(), which returns an error message instead of an error code.
1 parent bc0972e commit 4662151

9 files changed

+95
-49
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ PHP NEWS
4141
. Fixed bug #62017 (datefmt_create with incorrectly encoded timezone leaks
4242
pattern). (Gustavo)
4343
. Fixed bug #60785 (memory leak in IntlDateFormatter constructor). (Gustavo)
44+
45+
- JSON:
46+
. Improved error handling. (Nikita Popov)
4447

4548
- PDO:
4649
. Fixed bug #61755 (A parsing bug in the prepared statements can lead to

ext/json/json.c

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_json_decode, 0, 0, 1)
5151
ZEND_ARG_INFO(0, depth)
5252
ZEND_END_ARG_INFO()
5353

54-
ZEND_BEGIN_ARG_INFO(arginfo_json_last_error, 0)
54+
ZEND_BEGIN_ARG_INFO_EX(arginfo_json_last_error, 0, 0, 0)
55+
ZEND_ARG_INFO(0, as_string)
5556
ZEND_END_ARG_INFO()
5657
/* }}} */
5758

@@ -185,7 +186,6 @@ static void json_encode_array(smart_str *buf, zval **val, int options TSRMLS_DC)
185186

186187
if (myht && myht->nApplyCount > 1) {
187188
JSON_G(error_code) = PHP_JSON_ERROR_RECURSION;
188-
php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
189189
smart_str_appendl(buf, "null", 4);
190190
return;
191191
}
@@ -308,7 +308,6 @@ static void json_escape_string(smart_str *buf, char *s, int len, int options TSR
308308
efree(tmp);
309309
} else {
310310
JSON_G(error_code) = PHP_JSON_ERROR_INF_OR_NAN;
311-
php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec", d);
312311
smart_str_appendc(buf, '0');
313312
}
314313
}
@@ -326,7 +325,6 @@ static void json_escape_string(smart_str *buf, char *s, int len, int options TSR
326325
}
327326
if (len < 0) {
328327
JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
329-
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid UTF-8 sequence in argument");
330328
smart_str_appendl(buf, "null", 4);
331329
} else {
332330
smart_str_appendl(buf, "\"\"", 2);
@@ -466,7 +464,6 @@ PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options TSRMLS_
466464
efree(d);
467465
} else {
468466
JSON_G(error_code) = PHP_JSON_ERROR_INF_OR_NAN;
469-
php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec", dbl);
470467
smart_str_appendc(buf, '0');
471468
}
472469
}
@@ -483,7 +480,6 @@ PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options TSRMLS_
483480

484481
default:
485482
JSON_G(error_code) = PHP_JSON_ERROR_UNSUPPORTED_TYPE;
486-
php_error_docref(NULL TSRMLS_CC, E_WARNING, "type is unsupported");
487483
smart_str_appendl(buf, "null", 4);
488484
break;
489485
}
@@ -614,11 +610,40 @@ static PHP_FUNCTION(json_decode)
614610
Returns the error code of the last json_decode(). */
615611
static PHP_FUNCTION(json_last_error)
616612
{
617-
if (zend_parse_parameters_none() == FAILURE) {
613+
zend_bool as_string = 0;
614+
615+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &as_string) == FAILURE) {
618616
return;
619617
}
620618

621-
RETURN_LONG(JSON_G(error_code));
619+
/* return error code (JSON_ERROR_* constants) */
620+
if (!as_string) {
621+
RETURN_LONG(JSON_G(error_code));
622+
}
623+
624+
/* return error message (for debugging purposes) */
625+
switch(JSON_G(error_code)) {
626+
case PHP_JSON_ERROR_NONE:
627+
RETURN_STRING("No error", 1);
628+
case PHP_JSON_ERROR_DEPTH:
629+
RETURN_STRING("Maximum stack depth exceeded", 1);
630+
case PHP_JSON_ERROR_STATE_MISMATCH:
631+
RETURN_STRING("State mismatch (invalid or malformed JSON)", 1);
632+
case PHP_JSON_ERROR_CTRL_CHAR:
633+
RETURN_STRING("Control character error, possibly incorrectly encoded", 1);
634+
case PHP_JSON_ERROR_SYNTAX:
635+
RETURN_STRING("Syntax error", 1);
636+
case PHP_JSON_ERROR_UTF8:
637+
RETURN_STRING("Malformed UTF-8 characters, possibly incorrectly encoded", 1);
638+
case PHP_JSON_ERROR_RECURSION:
639+
RETURN_STRING("Recursion detected", 1);
640+
case PHP_JSON_ERROR_INF_OR_NAN:
641+
RETURN_STRING("Inf and NaN cannot be JSON encoded", 1);
642+
case PHP_JSON_ERROR_UNSUPPORTED_TYPE:
643+
RETURN_STRING("Type is not supported", 1);
644+
default:
645+
RETURN_STRING("Unknown error", 1);
646+
}
622647
}
623648
/* }}} */
624649

ext/json/tests/003.phpt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,17 @@ $a[] = &$a;
1010

1111
var_dump($a);
1212

13+
echo "\n";
14+
1315
var_dump(json_encode($a));
1416
var_dump(json_last_error());
17+
var_dump(json_last_error(true));
18+
19+
echo "\n";
1520

1621
var_dump(json_encode($a, JSON_PARTIAL_OUTPUT_ON_ERROR));
1722
var_dump(json_last_error());
23+
var_dump(json_last_error(true));
1824

1925
echo "Done\n";
2026
?>
@@ -27,11 +33,11 @@ array(1) {
2733
}
2834
}
2935

30-
Warning: json_encode(): recursion detected in %s on line %d
3136
bool(false)
3237
int(6)
38+
string(%d) "Recursion detected"
3339

34-
Warning: json_encode(): recursion detected in %s on line %d
3540
string(8) "[[null]]"
3641
int(6)
42+
string(%d) "Recursion detected"
3743
Done

ext/json/tests/004.phpt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,17 @@ $a->prop = $a;
1010

1111
var_dump($a);
1212

13+
echo "\n";
14+
1315
var_dump(json_encode($a));
1416
var_dump(json_last_error());
17+
var_dump(json_last_error(true));
18+
19+
echo "\n";
1520

1621
var_dump(json_encode($a, JSON_PARTIAL_OUTPUT_ON_ERROR));
1722
var_dump(json_last_error());
23+
var_dump(json_last_error(true));
1824

1925
echo "Done\n";
2026
?>
@@ -24,11 +30,11 @@ object(stdClass)#%d (1) {
2430
*RECURSION*
2531
}
2632

27-
Warning: json_encode(): recursion detected in %s on line %d
2833
bool(false)
2934
int(6)
35+
string(%d) "Recursion detected"
3036

31-
Warning: json_encode(): recursion detected in %s on line %d
3237
string(22) "{"prop":{"prop":null}}"
3338
int(6)
39+
string(%d) "Recursion detected"
3440
Done

ext/json/tests/bug54058.phpt

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,36 @@ $bad_utf8 = quoted_printable_decode('=B0');
99

1010
json_encode($bad_utf8);
1111
var_dump(json_last_error());
12+
var_dump(json_last_error(true));
1213

1314
$a = new stdclass;
1415
$a->foo = quoted_printable_decode('=B0');
1516
json_encode($a);
1617
var_dump(json_last_error());
18+
var_dump(json_last_error(true));
1719

1820
$b = new stdclass;
1921
$b->foo = $bad_utf8;
2022
$b->bar = 1;
2123
json_encode($b);
2224
var_dump(json_last_error());
25+
var_dump(json_last_error(true));
2326

2427
$c = array(
2528
'foo' => $bad_utf8,
2629
'bar' => 1
2730
);
2831
json_encode($c);
2932
var_dump(json_last_error());
33+
var_dump(json_last_error(true));
34+
3035
?>
3136
--EXPECTF--
32-
Warning: json_encode(): Invalid UTF-8 sequence in argument in %s on line %d
3337
int(5)
34-
35-
Warning: json_encode(): Invalid UTF-8 sequence in argument in %s on line %d
38+
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
3639
int(5)
37-
38-
Warning: json_encode(): Invalid UTF-8 sequence in argument in %s on line %d
40+
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
3941
int(5)
40-
41-
Warning: json_encode(): Invalid UTF-8 sequence in argument in %s on line %d
42+
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
4243
int(5)
44+
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"

ext/json/tests/bug61537.phpt

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,35 @@ Bug #61537 (json_encode() incorrectly truncates/discards information)
55
--FILE--
66
<?php
77
$invalid_utf8 = "\x9f";
8-
var_dump(json_encode($invalid_utf8), json_last_error());
9-
var_dump(json_encode($invalid_utf8, JSON_PARTIAL_OUTPUT_ON_ERROR), json_last_error());
8+
9+
var_dump(json_encode($invalid_utf8));
10+
var_dump(json_last_error(), json_last_error(true));
11+
12+
var_dump(json_encode($invalid_utf8, JSON_PARTIAL_OUTPUT_ON_ERROR));
13+
var_dump(json_last_error(), json_last_error(true));
14+
15+
echo "\n";
1016

1117
$invalid_utf8 = "an invalid sequen\xce in the middle of a string";
12-
var_dump(json_encode($invalid_utf8), json_last_error());
13-
var_dump(json_encode($invalid_utf8, JSON_PARTIAL_OUTPUT_ON_ERROR), json_last_error());
18+
19+
var_dump(json_encode($invalid_utf8));
20+
var_dump(json_last_error(), json_last_error(true));
21+
22+
var_dump(json_encode($invalid_utf8, JSON_PARTIAL_OUTPUT_ON_ERROR));
23+
var_dump(json_last_error(), json_last_error(true));
24+
1425
?>
1526
--EXPECTF--
16-
Warning: json_encode(): Invalid UTF-8 sequence in argument in %s on line %d
1727
bool(false)
1828
int(5)
19-
20-
Warning: json_encode(): Invalid UTF-8 sequence in argument in %s on line %d
29+
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
2130
string(4) "null"
2231
int(5)
32+
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
2333

24-
Warning: json_encode(): Invalid UTF-8 sequence in argument in %s on line %d
2534
bool(false)
2635
int(5)
27-
28-
Warning: json_encode(): Invalid UTF-8 sequence in argument in %s on line %d
36+
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
2937
string(4) "null"
3038
int(5)
39+
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"

ext/json/tests/inf_nan_error.phpt

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,36 @@ $inf = INF;
88
var_dump($inf);
99

1010
var_dump(json_encode($inf));
11-
var_dump(json_last_error());
11+
var_dump(json_last_error(), json_last_error(true));
1212

1313
var_dump(json_encode($inf, JSON_PARTIAL_OUTPUT_ON_ERROR));
14-
var_dump(json_last_error());
14+
var_dump(json_last_error(), json_last_error(true));
15+
16+
echo "\n";
1517

1618
$nan = NAN;
1719

1820
var_dump($nan);
1921

2022
var_dump(json_encode($nan));
21-
var_dump(json_last_error());
23+
var_dump(json_last_error(), json_last_error(true));
2224

2325
var_dump(json_encode($nan, JSON_PARTIAL_OUTPUT_ON_ERROR));
24-
var_dump(json_last_error());
26+
var_dump(json_last_error(), json_last_error(true));
2527
?>
2628
--EXPECTF--
2729
float(INF)
28-
29-
Warning: json_encode(): double INF does not conform to the JSON spec in %s on line %d
3030
bool(false)
3131
int(7)
32-
33-
Warning: json_encode(): double INF does not conform to the JSON spec in %s on line %d
32+
string(34) "Inf and NaN cannot be JSON encoded"
3433
string(1) "0"
3534
int(7)
36-
float(NAN)
35+
string(34) "Inf and NaN cannot be JSON encoded"
3736

38-
Warning: json_encode(): double NAN does not conform to the JSON spec in %s on line %d
37+
float(NAN)
3938
bool(false)
4039
int(7)
41-
42-
Warning: json_encode(): double NAN does not conform to the JSON spec in %s on line %d
40+
string(34) "Inf and NaN cannot be JSON encoded"
4341
string(1) "0"
4442
int(7)
43+
string(34) "Inf and NaN cannot be JSON encoded"

ext/json/tests/json_encode_basic.phpt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,6 @@ string(4) "null"
150150
-- Iteration 25 --
151151
string(4) "null"
152152
-- Iteration 26 --
153-
154-
Warning: json_encode(): type is unsupported in %s on line %d
155153
bool(false)
156154
-- Iteration 27 --
157155
string(82) "{"MyInt":99,"MyFloat":123.45,"MyBool":true,"MyNull":null,"MyString":"Hello World"}"

ext/json/tests/unsupported_type_error.phpt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,17 @@ $resource = fopen(__FILE__, "r");
88
var_dump($resource);
99

1010
var_dump(json_encode($resource));
11-
var_dump(json_last_error());
11+
var_dump(json_last_error(), json_last_error(true));
1212

1313
var_dump(json_encode($resource, JSON_PARTIAL_OUTPUT_ON_ERROR));
14-
var_dump(json_last_error());
14+
var_dump(json_last_error(), json_last_error(true));
1515

1616
?>
1717
--EXPECTF--
1818
resource(5) of type (stream)
19-
20-
Warning: json_encode(): type is unsupported in %s on line %d
2119
bool(false)
2220
int(8)
23-
24-
Warning: json_encode(): type is unsupported in %s on line %d
21+
string(21) "Type is not supported"
2522
string(4) "null"
2623
int(8)
24+
string(21) "Type is not supported"

0 commit comments

Comments
 (0)