Commit 68d0f569144 for php.net
commit 68d0f569144e5fbcc33fc9def4d8a9c3f6e0a828
Author: Tim Düsterhus <tim@tideways-gmbh.com>
Date: Sun Jul 12 21:05:09 2026 +0200
json: Report unterminated JSON strings as syntax errors (#22528)
Fixes php/php-src#22527.
diff --git a/NEWS b/NEWS
index 48b13b5bc90..0459373092f 100644
--- a/NEWS
+++ b/NEWS
@@ -58,6 +58,9 @@ PHP NEWS
IntlDateFormatter::localtime()/datefmt_localtime() now raise TypeError
when the offset argument is not of type int. (Weilin Du)
+- JSON:
+ . Report unterminated JSON strings as syntax errors. (timwolla)
+
- Opcache:
. Fixed bug GH-21770 (Infinite recursion in property hook getter in opcache
preloaded trait). (iliaal)
diff --git a/ext/json/json_scanner.re b/ext/json/json_scanner.re
index e4d25009132..0c64a6423ba 100644
--- a/ext/json/json_scanner.re
+++ b/ext/json/json_scanner.re
@@ -262,7 +262,14 @@ std:
s->errcode = PHP_JSON_ERROR_UTF8;
return PHP_JSON_T_ERROR;
}
-
+ <STR_P1>EOI {
+ if (s->limit < s->cursor) {
+ s->errcode = PHP_JSON_ERROR_SYNTAX;
+ } else {
+ s->errcode = PHP_JSON_ERROR_CTRL_CHAR;
+ }
+ return PHP_JSON_T_ERROR;
+ }
<STR_P1>CTRL {
s->errcode = PHP_JSON_ERROR_CTRL_CHAR;
return PHP_JSON_T_ERROR;
diff --git a/ext/json/tests/gh22527.phpt b/ext/json/tests/gh22527.phpt
new file mode 100644
index 00000000000..cb0dd982d50
--- /dev/null
+++ b/ext/json/tests/gh22527.phpt
@@ -0,0 +1,31 @@
+--TEST--
+GH-22527: Unterminated JSON strings are misleadingly reported as “Control character error”
+--FILE--
+<?php
+
+var_dump(json_decode('"'));
+var_dump(json_last_error(), json_last_error_msg());
+
+var_dump(json_decode('"123'));
+var_dump(json_last_error(), json_last_error_msg());
+
+var_dump(json_decode('{"123": "'));
+var_dump(json_last_error(), json_last_error_msg());
+
+var_dump(json_decode('"' . chr(0)));
+var_dump(json_last_error(), json_last_error_msg());
+
+?>
+--EXPECT--
+NULL
+int(4)
+string(30) "Syntax error near location 1:1"
+NULL
+int(4)
+string(30) "Syntax error near location 1:1"
+NULL
+int(4)
+string(30) "Syntax error near location 1:9"
+NULL
+int(3)
+string(71) "Control character error, possibly incorrectly encoded near location 1:1"
diff --git a/ext/json/tests/json_last_error_msg_error_location_001.phpt b/ext/json/tests/json_last_error_msg_error_location_001.phpt
index e0553f9f7d6..72cb1c42f23 100644
--- a/ext/json/tests/json_last_error_msg_error_location_001.phpt
+++ b/ext/json/tests/json_last_error_msg_error_location_001.phpt
@@ -66,8 +66,8 @@
Error at position 1:10:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:10"
+int(4)
+string(31) "Syntax error near location 1:10"
Error at position 1:9:
bool(false)
@@ -118,4 +118,3 @@
bool(false)
int(3)
string(72) "Control character error, possibly incorrectly encoded near location 1:10"
-
diff --git a/ext/json/tests/json_last_error_msg_error_location_002.phpt b/ext/json/tests/json_last_error_msg_error_location_002.phpt
index df7fc981ccb..31438255b31 100644
--- a/ext/json/tests/json_last_error_msg_error_location_002.phpt
+++ b/ext/json/tests/json_last_error_msg_error_location_002.phpt
@@ -53,51 +53,50 @@
Error after Japanese characters:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:12"
+int(4)
+string(31) "Syntax error near location 1:12"
Error after Russian characters:
bool(false)
-int(3)
-string(71) "Control character error, possibly incorrectly encoded near location 1:9"
+int(4)
+string(30) "Syntax error near location 1:9"
Error after Chinese characters:
bool(false)
-int(3)
-string(71) "Control character error, possibly incorrectly encoded near location 1:8"
+int(4)
+string(30) "Syntax error near location 1:8"
Error after Arabic characters:
bool(false)
-int(3)
-string(71) "Control character error, possibly incorrectly encoded near location 1:9"
+int(4)
+string(30) "Syntax error near location 1:9"
Error after Emoji:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:11"
+int(4)
+string(31) "Syntax error near location 1:11"
Error in mixed ASCII and UTF-8:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:27"
+int(4)
+string(31) "Syntax error near location 1:27"
Error with UTF-8 escaped sequences:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:10"
+int(4)
+string(31) "Syntax error near location 1:10"
Error in object with multiple UTF-8 keys:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:22"
+int(4)
+string(31) "Syntax error near location 1:22"
Error in array with UTF-8 strings:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:18"
+int(4)
+string(31) "Syntax error near location 1:18"
Error in nested object with UTF-8:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:15"
-
+int(4)
+string(31) "Syntax error near location 1:15"
diff --git a/ext/json/tests/json_last_error_msg_error_location_004.phpt b/ext/json/tests/json_last_error_msg_error_location_004.phpt
index 165449600fb..fcde3faa38a 100644
--- a/ext/json/tests/json_last_error_msg_error_location_004.phpt
+++ b/ext/json/tests/json_last_error_msg_error_location_004.phpt
@@ -53,8 +53,8 @@
Error in deeply nested object:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:31"
+int(4)
+string(31) "Syntax error near location 1:31"
Error in deeply nested array:
bool(true)
@@ -78,16 +78,15 @@
Error in complex structure:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:93"
+int(4)
+string(31) "Syntax error near location 1:93"
Error in array of objects:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:68"
+int(4)
+string(31) "Syntax error near location 1:68"
Error in object with array values:
bool(false)
int(2)
string(61) "State mismatch (invalid or malformed JSON) near location 1:82"
-
diff --git a/ext/json/tests/json_last_error_msg_error_location_005.phpt b/ext/json/tests/json_last_error_msg_error_location_005.phpt
index d12ce387e73..ee1800eb827 100644
--- a/ext/json/tests/json_last_error_msg_error_location_005.phpt
+++ b/ext/json/tests/json_last_error_msg_error_location_005.phpt
@@ -53,51 +53,50 @@
Error after UTF-16 escaped emoji:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:11"
+int(4)
+string(31) "Syntax error near location 1:11"
Error after multiple UTF-16 pairs:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:10"
+int(4)
+string(31) "Syntax error near location 1:10"
Error with mixed UTF-8 and UTF-16:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:11"
+int(4)
+string(31) "Syntax error near location 1:11"
Error with UTF-16 in key:
bool(false)
-int(3)
-string(71) "Control character error, possibly incorrectly encoded near location 1:9"
+int(4)
+string(30) "Syntax error near location 1:9"
Error with multiple UTF-16 keys:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:22"
+int(4)
+string(31) "Syntax error near location 1:22"
Error with BMP characters:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:10"
+int(4)
+string(31) "Syntax error near location 1:10"
Error with supplementary plane:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:11"
+int(4)
+string(31) "Syntax error near location 1:11"
Error in array with UTF-16:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:12"
+int(4)
+string(31) "Syntax error near location 1:12"
Error in nested structure with UTF-16:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:18"
+int(4)
+string(31) "Syntax error near location 1:18"
Error with UTF-16 and control chars:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:10"
-
+int(4)
+string(31) "Syntax error near location 1:10"
diff --git a/ext/json/tests/json_last_error_msg_error_location_006.phpt b/ext/json/tests/json_last_error_msg_error_location_006.phpt
index e6aab1af8f2..4a6c221c8f9 100644
--- a/ext/json/tests/json_last_error_msg_error_location_006.phpt
+++ b/ext/json/tests/json_last_error_msg_error_location_006.phpt
@@ -107,8 +107,8 @@
Error with very long key:
bool(false)
-int(3)
-string(73) "Control character error, possibly incorrectly encoded near location 1:506"
+int(4)
+string(32) "Syntax error near location 1:506"
Error after empty object:
bool(false)
@@ -149,4 +149,3 @@
bool(false)
int(3)
string(71) "Control character error, possibly incorrectly encoded near location 3:2"
-
diff --git a/ext/json/tests/json_last_error_msg_error_location_007.phpt b/ext/json/tests/json_last_error_msg_error_location_007.phpt
index 0e24889bbbb..168afc4dbfe 100644
--- a/ext/json/tests/json_last_error_msg_error_location_007.phpt
+++ b/ext/json/tests/json_last_error_msg_error_location_007.phpt
@@ -118,8 +118,8 @@
Unclosed string:
bool(false)
-int(3)
-string(71) "Control character error, possibly incorrectly encoded near location 1:9"
+int(4)
+string(30) "Syntax error near location 1:9"
Invalid escape sequence:
bool(false)
@@ -175,4 +175,3 @@
bool(false)
int(4)
string(30) "Syntax error near location 1:9"
-
diff --git a/ext/json/tests/json_last_error_msg_error_location_008.phpt b/ext/json/tests/json_last_error_msg_error_location_008.phpt
index 4d8a1012316..60dd9513472 100644
--- a/ext/json/tests/json_last_error_msg_error_location_008.phpt
+++ b/ext/json/tests/json_last_error_msg_error_location_008.phpt
@@ -82,101 +82,100 @@
Error with 2-byte UTF-8 (Latin Extended):
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:10"
+int(4)
+string(31) "Syntax error near location 1:10"
Error with 2-byte UTF-8 (Greek):
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:14"
+int(4)
+string(31) "Syntax error near location 1:14"
Error with 2-byte UTF-8 (Cyrillic):
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:12"
+int(4)
+string(31) "Syntax error near location 1:12"
Error with 3-byte UTF-8 (Chinese):
bool(false)
-int(3)
-string(71) "Control character error, possibly incorrectly encoded near location 1:8"
+int(4)
+string(30) "Syntax error near location 1:8"
Error with 3-byte UTF-8 (Japanese Hiragana):
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:10"
+int(4)
+string(31) "Syntax error near location 1:10"
Error with 3-byte UTF-8 (Japanese Katakana):
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:10"
+int(4)
+string(31) "Syntax error near location 1:10"
Error with 3-byte UTF-8 (Korean):
bool(false)
-int(3)
-string(71) "Control character error, possibly incorrectly encoded near location 1:8"
+int(4)
+string(30) "Syntax error near location 1:8"
Error with 4-byte UTF-8 (Emoji faces):
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:11"
+int(4)
+string(31) "Syntax error near location 1:11"
Error with 4-byte UTF-8 (Emoji objects):
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:13"
+int(4)
+string(31) "Syntax error near location 1:13"
Error with 4-byte UTF-8 (Mathematical symbols):
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:10"
+int(4)
+string(31) "Syntax error near location 1:10"
Error with mixed 1-2-3 byte UTF-8:
bool(false)
-int(3)
-string(71) "Control character error, possibly incorrectly encoded near location 1:9"
+int(4)
+string(30) "Syntax error near location 1:9"
Error with mixed 2-3-4 byte UTF-8:
bool(false)
-int(3)
-string(71) "Control character error, possibly incorrectly encoded near location 1:9"
+int(4)
+string(30) "Syntax error near location 1:9"
Error with all byte widths:
bool(false)
-int(3)
-string(71) "Control character error, possibly incorrectly encoded near location 1:9"
+int(4)
+string(30) "Syntax error near location 1:9"
Error with UTF-8 key at start:
bool(false)
-int(3)
-string(71) "Control character error, possibly incorrectly encoded near location 1:7"
+int(4)
+string(30) "Syntax error near location 1:7"
Error with multiple UTF-8 keys:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:35"
+int(4)
+string(31) "Syntax error near location 1:35"
Error in array with mixed UTF-8:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:25"
+int(4)
+string(31) "Syntax error near location 1:25"
Error in nested structure with various UTF-8:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:22"
+int(4)
+string(31) "Syntax error near location 1:22"
Error with combining diacritical marks:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:10"
+int(4)
+string(31) "Syntax error near location 1:10"
Error with Hebrew:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:10"
+int(4)
+string(31) "Syntax error near location 1:10"
Error with Arabic with diacritics:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:11"
-
+int(4)
+string(31) "Syntax error near location 1:11"
diff --git a/ext/json/tests/json_last_error_msg_error_location_009.phpt b/ext/json/tests/json_last_error_msg_error_location_009.phpt
index 406179693ef..9d4403838f6 100644
--- a/ext/json/tests/json_last_error_msg_error_location_009.phpt
+++ b/ext/json/tests/json_last_error_msg_error_location_009.phpt
@@ -75,18 +75,18 @@
Syntax error at deep nesting level:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:31"
+int(4)
+string(31) "Syntax error near location 1:31"
Syntax error in deep array:
bool(false)
-int(3)
-string(71) "Control character error, possibly incorrectly encoded near location 1:6"
+int(4)
+string(30) "Syntax error near location 1:6"
Error after valid deep structure:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:48"
+int(4)
+string(31) "Syntax error near location 1:48"
Error in middle of nested structure:
bool(false)
@@ -95,16 +95,15 @@
Error in array with nested objects:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:30"
+int(4)
+string(31) "Syntax error near location 1:30"
Error in deep UTF-8 structure:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:16"
+int(4)
+string(31) "Syntax error near location 1:16"
Valid deep structure within limit:
bool(true)
int(0)
string(8) "No error"
-
diff --git a/ext/json/tests/json_last_error_msg_error_location_010.phpt b/ext/json/tests/json_last_error_msg_error_location_010.phpt
index 10857020583..d187afff86c 100644
--- a/ext/json/tests/json_last_error_msg_error_location_010.phpt
+++ b/ext/json/tests/json_last_error_msg_error_location_010.phpt
@@ -119,13 +119,13 @@
Error in string with spaces:
bool(false)
-int(3)
-string(71) "Control character error, possibly incorrectly encoded near location 1:9"
+int(4)
+string(30) "Syntax error near location 1:9"
Error with whitespace around colon:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:12"
+int(4)
+string(31) "Syntax error near location 1:12"
Error with whitespace around comma:
bool(true)
@@ -154,11 +154,10 @@
Error in compact JSON:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:22"
+int(4)
+string(31) "Syntax error near location 1:22"
Error with regular spaces:
bool(false)
-int(3)
-string(72) "Control character error, possibly incorrectly encoded near location 1:10"
-
+int(4)
+string(31) "Syntax error near location 1:10"