Skip to content

Commit efa0ccf

Browse files
committed
ext/json: Stop encoding after a key fails to encode
A failure to escape an array or object key was ignored unless JSON_PARTIAL_OUTPUT_ON_ERROR was set, so encoding went on: later values still ran user code such as jsonSerialize(), and a later error replaced the one that was reported, e.g. a NAN value after an invalid UTF-8 key reported "Inf and NaN cannot be JSON encoded". Both key sites, the declared-property loop and the property table loop, now return FAILURE the same way a failed value does.
1 parent c8e6c86 commit efa0ccf

3 files changed

Lines changed: 61 additions & 4 deletions

File tree

‎NEWS‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ PHP NEWS
114114
(adapik)
115115
. Fixed json_encode() silently narrowing $depth values outside the int range,
116116
which wrapped to an unrelated depth. (cuishuang)
117+
. Fixed json_encode() continuing to encode after an array or object key
118+
failed to encode. (Ilia Alshanetsky)
117119

118120
- LDAP:
119121
. Fix crash in ldap_exop_sync() when $response_data is omitted. (lacatoire)

‎ext/json/json_encoder.c‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,11 +187,11 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
187187

188188
if (php_json_escape_string(buf, ZSTR_VAL(prop_info->name), ZSTR_LEN(prop_info->name),
189189
options & ~PHP_JSON_NUMERIC_CHECK, encoder) == FAILURE) {
190-
if (ZEND_HAS_OOM()) {
190+
if (ZEND_HAS_OOM() || !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
191191
PHP_JSON_HASH_UNPROTECT_RECURSION(obj);
192192
return FAILURE;
193193
}
194-
if ((options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) && buf->s) {
194+
if (buf->s) {
195195
ZEND_ASSERT(ZSTR_LEN(buf->s) >= 4);
196196
ZSTR_LEN(buf->s) -= 4;
197197
smart_str_appendl(buf, "\"\"", 2);
@@ -308,13 +308,13 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
308308

309309
if (php_json_escape_string(buf, ZSTR_VAL(key), ZSTR_LEN(key),
310310
options & ~PHP_JSON_NUMERIC_CHECK, encoder) == FAILURE) {
311-
if (ZEND_HAS_OOM()) {
311+
if (ZEND_HAS_OOM() || !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
312312
PHP_JSON_HASH_UNPROTECT_RECURSION(recursion_rc);
313313
zend_release_properties(prop_ht);
314314
zval_ptr_dtor(&tmp);
315315
return FAILURE;
316316
}
317-
if ((options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) && buf->s) {
317+
if (buf->s) {
318318
ZEND_ASSERT(ZSTR_LEN(buf->s) >= 4);
319319
ZSTR_LEN(buf->s) -= 4;
320320
smart_str_appendl(buf, "\"\"", 2);
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
--TEST--
2+
json_encode() stops encoding after an object or array key fails to encode
3+
--FILE--
4+
<?php
5+
6+
class Value implements JsonSerializable
7+
{
8+
public function jsonSerialize(): mixed
9+
{
10+
echo "jsonSerialize() called\n";
11+
return 1;
12+
}
13+
}
14+
15+
eval('class Declared { public $' . "\xff" . ' = 1; public $next; }');
16+
17+
$dynamic = new stdClass;
18+
$dynamic->{"\xff"} = 1;
19+
$dynamic->next = new Value;
20+
21+
$declared = new Declared;
22+
$declared->next = new Value;
23+
24+
$cases = [
25+
'array' => ["\xff" => 1, 'next' => new Value],
26+
'array, later NAN' => ["\xff" => 1, 'next' => NAN],
27+
'dynamic property' => $dynamic,
28+
'declared property' => $declared,
29+
];
30+
31+
foreach ($cases as $name => $value) {
32+
echo "$name:\n";
33+
var_dump(json_encode($value), json_last_error_msg());
34+
}
35+
36+
echo "partial output:\n";
37+
var_dump(json_encode(["\xff" => 1, 'next' => 2], JSON_PARTIAL_OUTPUT_ON_ERROR), json_last_error_msg());
38+
39+
?>
40+
--EXPECT--
41+
array:
42+
bool(false)
43+
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
44+
array, later NAN:
45+
bool(false)
46+
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
47+
dynamic property:
48+
bool(false)
49+
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
50+
declared property:
51+
bool(false)
52+
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
53+
partial output:
54+
string(15) "{"":1,"next":2}"
55+
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"

0 commit comments

Comments
 (0)