Skip to content

Commit 288e9c6

Browse files
committed
Merge remote-tracking branch 'ajf/JSONWhitespaceFix' into PHP-5.4
2 parents 064ba17 + 872ef91 commit 288e9c6

File tree

3 files changed

+149
-8
lines changed

3 files changed

+149
-8
lines changed

UPGRADING

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,16 @@ b. Extensions with changed behavior
492492
- the output of the tiger hash family has been corrected, see
493493
https://bugs.php.net/61307
494494

495+
- JSON extension
496+
- Fixed whitespace part of bug #64874 ("json_decode handles whitespace and
497+
case-sensitivity incorrectly")
498+
This means that leading and trailing whitespace when deserialising lone
499+
JSON null, true, false, string and number values no longer causes an
500+
error. Please note that this only applies to the deserialising of strings
501+
containing only a null, true, false, string or number value. Leading and
502+
trailing whitespace around and inside objects and arrays has never caused
503+
an error.
504+
495505
===========================
496506
10. Changes in SAPI support
497507
===========================

ext/json/json.c

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -684,21 +684,35 @@ PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len,
684684
double d;
685685
int type, overflow_info;
686686
long p;
687+
char *trim = str;
688+
int trim_len = str_len;
689+
690+
/* Increment trimmed string pointer to strip leading whitespace */
691+
/* JSON RFC says to consider as whitespace: space, tab, LF or CR */
692+
while (trim_len && (*trim == ' ' || *trim == '\t' || *trim == '\n' || *trim == '\r')) {
693+
trim++;
694+
trim_len--;
695+
}
696+
697+
/* Decrement trimmed string length to strip trailing whitespace */
698+
while (trim_len && (trim[trim_len - 1] == ' ' || trim[trim_len - 1] == '\t' || trim[trim_len - 1] == '\n' || trim[trim_len - 1] == '\r')) {
699+
trim_len--;
700+
}
687701

688702
RETVAL_NULL();
689-
if (str_len == 4) {
690-
if (!strcasecmp(str, "null")) {
703+
if (trim_len == 4) {
704+
if (!strncasecmp(trim, "null", trim_len)) {
691705
/* We need to explicitly clear the error because its an actual NULL and not an error */
692706
jp->error_code = PHP_JSON_ERROR_NONE;
693707
RETVAL_NULL();
694-
} else if (!strcasecmp(str, "true")) {
708+
} else if (!strncasecmp(trim, "true", trim_len)) {
695709
RETVAL_BOOL(1);
696710
}
697-
} else if (str_len == 5 && !strcasecmp(str, "false")) {
711+
} else if (trim_len == 5 && !strncasecmp(trim, "false", trim_len)) {
698712
RETVAL_BOOL(0);
699713
}
700714

701-
if ((type = is_numeric_string_ex(str, str_len, &p, &d, 0, &overflow_info)) != 0) {
715+
if ((type = is_numeric_string_ex(trim, trim_len, &p, &d, 0, &overflow_info)) != 0) {
702716
if (type == IS_LONG) {
703717
RETVAL_LONG(p);
704718
} else if (type == IS_DOUBLE) {
@@ -711,10 +725,10 @@ PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len,
711725
int i;
712726
zend_bool is_float = 0;
713727

714-
for (i = (str[0] == '-' ? 1 : 0); i < str_len; i++) {
728+
for (i = (trim[0] == '-' ? 1 : 0); i < trim_len; i++) {
715729
/* Not using isdigit() because it's locale specific,
716730
* but we expect JSON input to always be UTF-8. */
717-
if (str[i] < '0' || str[i] > '9') {
731+
if (trim[i] < '0' || trim[i] > '9') {
718732
is_float = 1;
719733
break;
720734
}
@@ -723,7 +737,7 @@ PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len,
723737
if (is_float) {
724738
RETVAL_DOUBLE(d);
725739
} else {
726-
RETVAL_STRINGL(str, str_len, 1);
740+
RETVAL_STRINGL(trim, trim_len, 1);
727741
}
728742
} else {
729743
RETVAL_DOUBLE(d);

ext/json/tests/bug64874_part1.phpt

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
--TEST--
2+
Whitespace part of bug #64874 ("json_decode handles whitespace and case-sensitivity incorrectly")
3+
--SKIPIF--
4+
<?php if (!extension_loaded("json")) print "skip"; ?>
5+
--FILE--
6+
<?php
7+
function decode($json) {
8+
var_dump(json_decode($json));
9+
var_dump(json_last_error() !== 0);
10+
echo "\n";
11+
}
12+
13+
// Leading whitespace should be ignored
14+
decode(" true");
15+
decode("\ttrue");
16+
decode("\ntrue");
17+
decode("\rtrue");
18+
19+
// So should trailing whitespace
20+
decode("true ");
21+
decode("true\t");
22+
decode("true\n");
23+
decode("true\r");
24+
25+
// And so should the combination of both
26+
decode(" true ");
27+
decode(" true\t");
28+
decode(" true\n");
29+
decode(" true\r");
30+
decode("\ttrue ");
31+
decode("\ttrue\t");
32+
decode("\ttrue\n");
33+
decode("\ttrue\r");
34+
decode("\ntrue ");
35+
decode("\ntrue\t");
36+
decode("\ntrue\n");
37+
decode("\ntrue\r");
38+
decode("\rtrue ");
39+
decode("\rtrue\t");
40+
decode("\rtrue\n");
41+
decode("\rtrue\r");
42+
43+
echo "Done\n";
44+
--EXPECT--
45+
bool(true)
46+
bool(false)
47+
48+
bool(true)
49+
bool(false)
50+
51+
bool(true)
52+
bool(false)
53+
54+
bool(true)
55+
bool(false)
56+
57+
bool(true)
58+
bool(false)
59+
60+
bool(true)
61+
bool(false)
62+
63+
bool(true)
64+
bool(false)
65+
66+
bool(true)
67+
bool(false)
68+
69+
bool(true)
70+
bool(false)
71+
72+
bool(true)
73+
bool(false)
74+
75+
bool(true)
76+
bool(false)
77+
78+
bool(true)
79+
bool(false)
80+
81+
bool(true)
82+
bool(false)
83+
84+
bool(true)
85+
bool(false)
86+
87+
bool(true)
88+
bool(false)
89+
90+
bool(true)
91+
bool(false)
92+
93+
bool(true)
94+
bool(false)
95+
96+
bool(true)
97+
bool(false)
98+
99+
bool(true)
100+
bool(false)
101+
102+
bool(true)
103+
bool(false)
104+
105+
bool(true)
106+
bool(false)
107+
108+
bool(true)
109+
bool(false)
110+
111+
bool(true)
112+
bool(false)
113+
114+
bool(true)
115+
bool(false)
116+
117+
Done

0 commit comments

Comments
 (0)