Commit b80ffc5b2ea for php.net
commit b80ffc5b2ead9a91327edebd2760c8f2b927ed38
Author: Juan Morales <jcmargentina@gmail.com>
Date: Sat Jan 3 14:34:19 2026 -0300
Json last error msg/error message with location error (#20629)
This slightly extend error messages with locations taken from scanner / parser
diff --git a/ext/json/json.c b/ext/json/json.c
index 9f91d39594e..079f67a5c40 100644
--- a/ext/json/json.c
+++ b/ext/json/json.c
@@ -63,6 +63,8 @@ static PHP_GINIT_FUNCTION(json)
#endif
json_globals->encoder_depth = 0;
json_globals->error_code = 0;
+ json_globals->error_line = 0;
+ json_globals->error_column = 0;
json_globals->encode_max_depth = PHP_JSON_PARSER_DEFAULT_DEPTH;
}
/* }}} */
@@ -70,6 +72,8 @@ static PHP_GINIT_FUNCTION(json)
static PHP_RINIT_FUNCTION(json)
{
JSON_G(error_code) = 0;
+ JSON_G(error_line) = 0;
+ JSON_G(error_column) = 0;
return SUCCESS;
}
@@ -177,6 +181,18 @@ static const char *php_json_get_error_msg(php_json_error_code error_code) /* {{{
}
/* }}} */
+static zend_string *php_json_get_error_msg_with_location(php_json_error_code error_code, size_t line, size_t column) /* {{{ */
+{
+ const char *base_msg = php_json_get_error_msg(error_code);
+
+ if (line > 0 && column > 0) {
+ return zend_strpprintf(0, "%s near location %zu:%zu", base_msg, line, column);
+ }
+
+ return zend_string_init(base_msg, strlen(base_msg), 0);
+}
+/* }}} */
+
PHP_JSON_API zend_result php_json_decode_ex(zval *return_value, const char *str, size_t str_len, zend_long options, zend_long depth) /* {{{ */
{
php_json_parser parser;
@@ -185,10 +201,17 @@ PHP_JSON_API zend_result php_json_decode_ex(zval *return_value, const char *str,
if (php_json_yyparse(&parser)) {
php_json_error_code error_code = php_json_parser_error_code(&parser);
+ size_t error_line = php_json_parser_error_line(&parser);
+ size_t error_column = php_json_parser_error_column(&parser);
+
if (!(options & PHP_JSON_THROW_ON_ERROR)) {
JSON_G(error_code) = error_code;
+ JSON_G(error_line) = error_line;
+ JSON_G(error_column) = error_column;
} else {
- zend_throw_exception(php_json_exception_ce, php_json_get_error_msg(error_code), error_code);
+ zend_string *error_msg = php_json_get_error_msg_with_location(error_code, error_line, error_column);
+ zend_throw_exception(php_json_exception_ce, ZSTR_VAL(error_msg), error_code);
+ zend_string_release(error_msg);
}
RETVAL_NULL();
return FAILURE;
@@ -208,7 +231,12 @@ 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);
+ size_t error_line = php_json_parser_error_line(&parser);
+ size_t error_column = php_json_parser_error_column(&parser);
+
JSON_G(error_code) = error_code;
+ JSON_G(error_line) = error_line;
+ JSON_G(error_column) = error_column;
return false;
}
@@ -274,11 +302,15 @@ PHP_FUNCTION(json_decode)
if (!(options & PHP_JSON_THROW_ON_ERROR)) {
JSON_G(error_code) = PHP_JSON_ERROR_NONE;
+ JSON_G(error_line) = 0;
+ JSON_G(error_column) = 0;
}
if (!str_len) {
if (!(options & PHP_JSON_THROW_ON_ERROR)) {
JSON_G(error_code) = PHP_JSON_ERROR_SYNTAX;
+ JSON_G(error_line) = 0;
+ JSON_G(error_column) = 0;
} else {
zend_throw_exception(php_json_exception_ce, php_json_get_error_msg(PHP_JSON_ERROR_SYNTAX), PHP_JSON_ERROR_SYNTAX);
}
@@ -331,10 +363,14 @@ PHP_FUNCTION(json_validate)
if (!str_len) {
JSON_G(error_code) = PHP_JSON_ERROR_SYNTAX;
+ JSON_G(error_line) = 0;
+ JSON_G(error_column) = 0;
RETURN_FALSE;
}
JSON_G(error_code) = PHP_JSON_ERROR_NONE;
+ JSON_G(error_line) = 0;
+ JSON_G(error_column) = 0;
if (depth <= 0) {
zend_argument_value_error(2, "must be greater than 0");
@@ -364,6 +400,10 @@ PHP_FUNCTION(json_last_error_msg)
{
ZEND_PARSE_PARAMETERS_NONE();
- RETURN_STRING(php_json_get_error_msg(JSON_G(error_code)));
+ RETVAL_STR(php_json_get_error_msg_with_location(
+ JSON_G(error_code),
+ JSON_G(error_line),
+ JSON_G(error_column)
+ ));
}
/* }}} */
diff --git a/ext/json/json_parser.y b/ext/json/json_parser.y
index d570cddc91e..2fd4edfe369 100644
--- a/ext/json/json_parser.y
+++ b/ext/json/json_parser.y
@@ -41,6 +41,7 @@ int json_yydebug = 1;
}
+%locations
%define api.prefix {php_json_yy}
%define api.pure full
%param { php_json_parser *parser }
@@ -49,7 +50,6 @@ int json_yydebug = 1;
zval value;
}
-
%token <value> PHP_JSON_T_NUL
%token <value> PHP_JSON_T_TRUE
%token <value> PHP_JSON_T_FALSE
@@ -66,8 +66,8 @@ int json_yydebug = 1;
%destructor { zval_ptr_dtor_nogc(&$$); } <value>
%code {
-static int php_json_yylex(union YYSTYPE *value, php_json_parser *parser);
-static void php_json_yyerror(php_json_parser *parser, char const *msg);
+static int php_json_yylex(union YYSTYPE *value, YYLTYPE *location, php_json_parser *parser);
+static void php_json_yyerror(YYLTYPE *location, php_json_parser *parser, char const *msg);
static int php_json_parser_array_create(php_json_parser *parser, zval *array);
static int php_json_parser_object_create(php_json_parser *parser, zval *array);
@@ -277,7 +277,7 @@ static int php_json_parser_object_update_validate(php_json_parser *parser, zval
return SUCCESS;
}
-static int php_json_yylex(union YYSTYPE *value, php_json_parser *parser)
+static int php_json_yylex(union YYSTYPE *value, YYLTYPE *location, php_json_parser *parser)
{
int token = php_json_scan(&parser->scanner);
@@ -293,10 +293,15 @@ static int php_json_yylex(union YYSTYPE *value, php_json_parser *parser)
value->value = parser->scanner.value;
}
+ location->first_column = PHP_JSON_SCANNER_LOCATION(parser->scanner, first_column);
+ location->first_line = PHP_JSON_SCANNER_LOCATION(parser->scanner, first_line);
+ location->last_column = PHP_JSON_SCANNER_LOCATION(parser->scanner, last_column);
+ location->last_line = PHP_JSON_SCANNER_LOCATION(parser->scanner, last_line);
+
return token;
}
-static void php_json_yyerror(php_json_parser *parser, char const *msg)
+static void php_json_yyerror(YYLTYPE *location, php_json_parser *parser, char const *msg)
{
if (!parser->scanner.errcode) {
parser->scanner.errcode = PHP_JSON_ERROR_SYNTAX;
@@ -308,6 +313,16 @@ PHP_JSON_API php_json_error_code php_json_parser_error_code(const php_json_parse
return parser->scanner.errcode;
}
+PHP_JSON_API size_t php_json_parser_error_line(const php_json_parser *parser)
+{
+ return parser->scanner.errloc.first_line;
+}
+
+PHP_JSON_API size_t php_json_parser_error_column(const php_json_parser *parser)
+{
+ return parser->scanner.errloc.first_column;
+}
+
static const php_json_parser_methods default_parser_methods =
{
php_json_parser_array_create,
diff --git a/ext/json/json_scanner.re b/ext/json/json_scanner.re
index 0debb3b03cb..d6eaaf65b2e 100644
--- a/ext/json/json_scanner.re
+++ b/ext/json/json_scanner.re
@@ -52,6 +52,8 @@
#define PHP_JSON_INT_MAX_LENGTH (MAX_LENGTH_OF_LONG - 1)
+#define PHP_JSON_TOKEN_LENGTH() ((size_t) (s->cursor - s->token))
+#define PHP_JSON_TOKEN_LOCATION(location) (s)->errloc.location
static void php_json_scanner_copy_string(php_json_scanner *s, size_t esc_size)
{
@@ -96,6 +98,10 @@ 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;
+ PHP_JSON_TOKEN_LOCATION(first_column) = 1;
+ PHP_JSON_TOKEN_LOCATION(first_line) = 1;
+ PHP_JSON_TOKEN_LOCATION(last_column) = 1;
+ PHP_JSON_TOKEN_LOCATION(last_line) = 1;
PHP_JSON_CONDITION_SET(JS);
}
@@ -104,6 +110,8 @@ int php_json_scan(php_json_scanner *s)
ZVAL_NULL(&s->value);
std:
+ PHP_JSON_TOKEN_LOCATION(first_column) = s->errloc.last_column;
+ PHP_JSON_TOKEN_LOCATION(first_line) = s->errloc.last_line;
s->token = s->cursor;
/*!re2c
@@ -149,27 +157,50 @@ std:
UTF16_3 = UTFPREF ( ( ( HEXC | [efEF] ) HEX ) | ( [dD] HEX7 ) ) HEX{2} ;
UTF16_4 = UTFPREF [dD] [89abAB] HEX{2} UTFPREF [dD] [c-fC-F] HEX{2} ;
- <JS>"{" { return '{'; }
- <JS>"}" { return '}'; }
- <JS>"[" { return '['; }
- <JS>"]" { return ']'; }
- <JS>":" { return ':'; }
- <JS>"," { return ','; }
+ <JS>"{" {
+ PHP_JSON_TOKEN_LOCATION(last_column)++;
+ return '{';
+ }
+ <JS>"}" {
+ PHP_JSON_TOKEN_LOCATION(last_column)++;
+ return '}';
+ }
+ <JS>"[" {
+ PHP_JSON_TOKEN_LOCATION(last_column)++;
+ return '[';
+ }
+ <JS>"]" {
+ PHP_JSON_TOKEN_LOCATION(last_column)++;
+ return ']';
+ }
+ <JS>":" {
+ PHP_JSON_TOKEN_LOCATION(last_column)++;
+ return ':';
+ }
+ <JS>"," {
+ PHP_JSON_TOKEN_LOCATION(last_column)++;
+ return ',';
+ }
<JS>"null" {
+ PHP_JSON_TOKEN_LOCATION(last_column) += 4;
ZVAL_NULL(&s->value);
return PHP_JSON_T_NUL;
}
<JS>"true" {
+ PHP_JSON_TOKEN_LOCATION(last_column) += 4;
ZVAL_TRUE(&s->value);
return PHP_JSON_T_TRUE;
}
<JS>"false" {
+ PHP_JSON_TOKEN_LOCATION(last_column) += 5;
ZVAL_FALSE(&s->value);
return PHP_JSON_T_FALSE;
}
<JS>INT {
bool bigint = 0, negative = s->token[0] == '-';
- size_t digits = (size_t) (s->cursor - s->token - negative);
+ size_t digits = PHP_JSON_TOKEN_LENGTH();
+ PHP_JSON_TOKEN_LOCATION(last_column) += digits;
+ digits -= negative;
if (digits >= PHP_JSON_INT_MAX_LENGTH) {
if (digits == PHP_JSON_INT_MAX_LENGTH) {
int cmp = strncmp((char *) (s->token + negative), LONG_MIN_DIGITS, PHP_JSON_INT_MAX_LENGTH);
@@ -192,10 +223,19 @@ std:
}
}
<JS>FLOAT|EXP {
+ PHP_JSON_TOKEN_LOCATION(last_column) += PHP_JSON_TOKEN_LENGTH();
ZVAL_DOUBLE(&s->value, zend_strtod((char *) s->token, NULL));
return PHP_JSON_T_DOUBLE;
}
- <JS>NL|WS { goto std; }
+ <JS>NL {
+ PHP_JSON_TOKEN_LOCATION(last_line)++;
+ PHP_JSON_TOKEN_LOCATION(last_column) = 1;
+ goto std;
+ }
+ <JS>WS {
+ PHP_JSON_TOKEN_LOCATION(last_column) += PHP_JSON_TOKEN_LENGTH();
+ goto std;
+ }
<JS>EOI {
if (s->limit < s->cursor) {
return PHP_JSON_T_EOI;
@@ -205,6 +245,7 @@ std:
}
}
<JS>["] {
+ PHP_JSON_TOKEN_LOCATION(last_column)++;
s->str_start = s->cursor;
s->str_esc = 0;
s->utf8_invalid = 0;
@@ -229,18 +270,22 @@ std:
return PHP_JSON_T_ERROR;
}
<STR_P1>UTF16_1 {
+ PHP_JSON_TOKEN_LOCATION(last_column) += 1;
s->str_esc += 5;
PHP_JSON_CONDITION_GOTO(STR_P1);
}
<STR_P1>UTF16_2 {
+ PHP_JSON_TOKEN_LOCATION(last_column) += 1;
s->str_esc += 4;
PHP_JSON_CONDITION_GOTO(STR_P1);
}
<STR_P1>UTF16_3 {
+ PHP_JSON_TOKEN_LOCATION(last_column) += 1;
s->str_esc += 3;
PHP_JSON_CONDITION_GOTO(STR_P1);
}
<STR_P1>UTF16_4 {
+ PHP_JSON_TOKEN_LOCATION(last_column) += 1;
s->str_esc += 8;
PHP_JSON_CONDITION_GOTO(STR_P1);
}
@@ -249,6 +294,7 @@ std:
return PHP_JSON_T_ERROR;
}
<STR_P1>ESC {
+ PHP_JSON_TOKEN_LOCATION(last_column) += 2;
s->str_esc++;
PHP_JSON_CONDITION_GOTO(STR_P1);
}
@@ -257,6 +303,7 @@ std:
return PHP_JSON_T_ERROR;
}
<STR_P1>["] {
+ PHP_JSON_TOKEN_LOCATION(last_column)++;
zend_string *str;
size_t len = (size_t)(s->cursor - s->str_start - s->str_esc - 1 + s->utf8_invalid_count);
if (len == 0) {
@@ -277,7 +324,22 @@ std:
return PHP_JSON_T_STRING;
}
}
- <STR_P1>UTF8 { PHP_JSON_CONDITION_GOTO(STR_P1); }
+ <STR_P1>UTF8_1 {
+ PHP_JSON_TOKEN_LOCATION(last_column)++;
+ PHP_JSON_CONDITION_GOTO(STR_P1);
+ }
+ <STR_P1>UTF8_2 {
+ PHP_JSON_TOKEN_LOCATION(last_column) += 1;
+ PHP_JSON_CONDITION_GOTO(STR_P1);
+ }
+ <STR_P1>UTF8_3 {
+ PHP_JSON_TOKEN_LOCATION(last_column) += 1;
+ PHP_JSON_CONDITION_GOTO(STR_P1);
+ }
+ <STR_P1>UTF8_4 {
+ PHP_JSON_TOKEN_LOCATION(last_column) += 1;
+ PHP_JSON_CONDITION_GOTO(STR_P1);
+ }
<STR_P1>ANY {
if (s->options & (PHP_JSON_INVALID_UTF8_IGNORE | PHP_JSON_INVALID_UTF8_SUBSTITUTE)) {
if (s->options & PHP_JSON_INVALID_UTF8_SUBSTITUTE) {
@@ -295,7 +357,6 @@ std:
s->errcode = PHP_JSON_ERROR_UTF8;
return PHP_JSON_T_ERROR;
}
-
<STR_P2_UTF,STR_P2_BIN>UTF16_1 {
int utf16 = php_json_ucs2_to_int(s, 2);
PHP_JSON_SCANNER_COPY_UTF();
diff --git a/ext/json/php_json.h b/ext/json/php_json.h
index b79c7c836f7..bbe8be9d60a 100644
--- a/ext/json/php_json.h
+++ b/ext/json/php_json.h
@@ -86,6 +86,8 @@ ZEND_BEGIN_MODULE_GLOBALS(json)
int encoder_depth;
int encode_max_depth;
php_json_error_code error_code;
+ size_t error_line;
+ size_t error_column;
ZEND_END_MODULE_GLOBALS(json)
PHP_JSON_API ZEND_EXTERN_MODULE_GLOBALS(json)
diff --git a/ext/json/php_json_parser.h b/ext/json/php_json_parser.h
index 8aedce9ac55..8fee3d11c6b 100644
--- a/ext/json/php_json_parser.h
+++ b/ext/json/php_json_parser.h
@@ -50,12 +50,20 @@ typedef struct _php_json_parser_methods {
php_json_parser_func_object_end_t object_end;
} php_json_parser_methods;
+ typedef struct _php_json_parser_location {
+ size_t first_line;
+ size_t first_column;
+ size_t last_line;
+ size_t last_column;
+} php_json_parser_location;
+
struct _php_json_parser {
php_json_scanner scanner;
zval *return_value;
int depth;
int max_depth;
php_json_parser_methods methods;
+ php_json_parser_location *location;
};
PHP_JSON_API void php_json_parser_init_ex(
@@ -77,6 +85,10 @@ 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 size_t php_json_parser_error_line(const php_json_parser *parser);
+
+PHP_JSON_API size_t php_json_parser_error_column(const php_json_parser *parser);
+
PHP_JSON_API int php_json_parse(php_json_parser *parser);
int php_json_yyparse(php_json_parser *parser);
diff --git a/ext/json/php_json_scanner.h b/ext/json/php_json_scanner.h
index a49be68cd63..a6de149391d 100644
--- a/ext/json/php_json_scanner.h
+++ b/ext/json/php_json_scanner.h
@@ -22,6 +22,17 @@
typedef unsigned char php_json_ctype;
+typedef struct _php_json_error_location {
+ /** first column of the error */
+ size_t first_column;
+ /** first line of the error */
+ size_t first_line;
+ /** last column of the error */
+ size_t last_column;
+ /** last line of the error */
+ size_t last_line;
+} php_json_error_location;
+
typedef struct _php_json_scanner {
php_json_ctype *cursor; /* cursor position */
php_json_ctype *token; /* token position */
@@ -35,10 +46,12 @@ typedef struct _php_json_scanner {
int state; /* condition state */
int options; /* options */
php_json_error_code errcode; /* error type if there is an error */
+ php_json_error_location errloc; /* error location */
int utf8_invalid; /* whether utf8 is invalid */
int utf8_invalid_count; /* number of extra character for invalid utf8 */
} php_json_scanner;
+#define PHP_JSON_SCANNER_LOCATION(scanner, slocation) (scanner).errloc.slocation
void php_json_scanner_init(php_json_scanner *scanner, const char *str, size_t str_len, int options);
int php_json_scan(php_json_scanner *s);
diff --git a/ext/json/tests/007.phpt b/ext/json/tests/007.phpt
index dea641317e9..9a5ae654d63 100644
--- a/ext/json/tests/007.phpt
+++ b/ext/json/tests/007.phpt
@@ -24,14 +24,14 @@
string(8) "No error"
NULL
int(1)
-string(28) "Maximum stack depth exceeded"
+string(46) "Maximum stack depth exceeded near location 1:2"
NULL
int(2)
-string(42) "State mismatch (invalid or malformed JSON)"
+string(60) "State mismatch (invalid or malformed JSON) near location 1:3"
NULL
int(3)
-string(53) "Control character error, possibly incorrectly encoded"
+string(71) "Control character error, possibly incorrectly encoded near location 1:2"
NULL
int(4)
-string(12) "Syntax error"
+string(30) "Syntax error near location 1:3"
Done
diff --git a/ext/json/tests/bug62010.phpt b/ext/json/tests/bug62010.phpt
index 2591231dcdd..862d7dc7e2c 100644
--- a/ext/json/tests/bug62010.phpt
+++ b/ext/json/tests/bug62010.phpt
@@ -10,4 +10,4 @@
--EXPECT--
NULL
bool(true)
-string(50) "Single unpaired UTF-16 surrogate in unicode escape"
+string(68) "Single unpaired UTF-16 surrogate in unicode escape near location 1:1"
diff --git a/ext/json/tests/bug68546.phpt b/ext/json/tests/bug68546.phpt
index 8835a72c5ea..1847eabf3a8 100644
--- a/ext/json/tests/bug68546.phpt
+++ b/ext/json/tests/bug68546.phpt
@@ -5,7 +5,7 @@
var_dump(json_decode('{"key": {"\u0000": "aa"}}'));
var_dump(json_last_error() === JSON_ERROR_INVALID_PROPERTY_NAME);
-var_dump(json_decode('[{"key1": 0, "\u0000": 1}]'));
+var_dump(json_decode('[{"key1": 0, "\u1234": 1, "\u0000": 1}]'));
var_dump(json_last_error() === JSON_ERROR_INVALID_PROPERTY_NAME);
var_dump(json_last_error_msg());
@@ -16,5 +16,5 @@
bool(true)
NULL
bool(true)
-string(36) "The decoded property name is invalid"
+string(55) "The decoded property name is invalid near location 1:27"
Done
diff --git a/ext/json/tests/json_decode_exceptions.phpt b/ext/json/tests/json_decode_exceptions.phpt
index 7dc2e7408a0..d53941682e4 100644
--- a/ext/json/tests/json_decode_exceptions.phpt
+++ b/ext/json/tests/json_decode_exceptions.phpt
@@ -13,7 +13,7 @@
--EXPECTF--
object(JsonException)#1 (7) {
["message":protected]=>
- string(12) "Syntax error"
+ string(30) "Syntax error near location 1:2"
["string":"Exception":private]=>
string(0) ""
["code":protected]=>
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
new file mode 100644
index 00000000000..e0553f9f7d6
--- /dev/null
+++ b/ext/json/tests/json_last_error_msg_error_location_001.phpt
@@ -0,0 +1,121 @@
+--TEST--
+json_last_error_msg() - Error location reporting with ASCII characters
+--FILE--
+<?php
+
+require_once("json_validate_requires.inc");
+
+echo "Testing errors at various locations with ASCII characters\n\n";
+
+// Error at start
+echo "Error at position 1:1:\n";
+json_validate_trycatchdump("-");
+
+// Error in middle of simple object
+echo "\nError at position 1:10:\n";
+json_validate_trycatchdump('{"name": "value}');
+
+// Missing colon
+echo "\nError at position 1:9:\n";
+json_validate_trycatchdump('{"name" "value"}');
+
+// Extra comma
+echo "\nError at position 1:16:\n";
+json_validate_trycatchdump('{"name": "val",}');
+
+// Missing closing bracket in array
+echo "\nError at position 1:15:\n";
+json_validate_trycatchdump('[1, 2, 3, 4, 5');
+
+// Invalid value
+echo "\nError at position 1:10:\n";
+json_validate_trycatchdump('{"test": undefined}');
+
+// Trailing comma in array
+echo "\nError at position 1:7:\n";
+json_validate_trycatchdump('[1, 2,]');
+
+// Single quote instead of double quote
+echo "\nError at position 1:2:\n";
+json_validate_trycatchdump("{'key': 'value'}");
+
+// Missing closing brace
+echo "\nError at position 1:16:\n";
+json_validate_trycatchdump('{"key": "value"');
+
+// Double comma
+echo "\nError at position 1:4:\n";
+json_validate_trycatchdump('[1,, 2]');
+
+// Invalid escape sequence
+echo "\nError at position 1:10:\n";
+json_validate_trycatchdump('{"test": "\x"}');
+
+// Unescaped control character
+echo "\nError at position 1:10:\n";
+json_validate_trycatchdump('{"test": "' . "\n" . '"}');
+
+?>
+--EXPECT--
+Testing errors at various locations with ASCII characters
+
+Error at position 1:1:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:1"
+
+Error at position 1:10:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded near location 1:10"
+
+Error at position 1:9:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:9"
+
+Error at position 1:16:
+bool(false)
+int(4)
+string(31) "Syntax error near location 1:16"
+
+Error at position 1:15:
+bool(false)
+int(4)
+string(31) "Syntax error near location 1:15"
+
+Error at position 1:10:
+bool(false)
+int(4)
+string(31) "Syntax error near location 1:10"
+
+Error at position 1:7:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:7"
+
+Error at position 1:2:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:2"
+
+Error at position 1:16:
+bool(false)
+int(4)
+string(31) "Syntax error near location 1:16"
+
+Error at position 1:4:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:4"
+
+Error at position 1:10:
+bool(false)
+int(4)
+string(31) "Syntax error near location 1:10"
+
+Error at position 1:10:
+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
new file mode 100644
index 00000000000..df7fc981ccb
--- /dev/null
+++ b/ext/json/tests/json_last_error_msg_error_location_002.phpt
@@ -0,0 +1,103 @@
+--TEST--
+json_last_error_msg() - Error location reporting with Unicode UTF-8 characters
+--FILE--
+<?php
+
+require_once("json_validate_requires.inc");
+
+echo "Testing error locations with Unicode UTF-8 characters\n\n";
+
+// Error after UTF-8 characters (Japanese)
+echo "Error after Japanese characters:\n";
+json_validate_trycatchdump('{"プレスキット": "value}');
+
+// Error after UTF-8 characters (Russian)
+echo "\nError after Russian characters:\n";
+json_validate_trycatchdump('{"имя": "значение}');
+
+// Error after UTF-8 characters (Chinese)
+echo "\nError after Chinese characters:\n";
+json_validate_trycatchdump('{"名字": "值}');
+
+// Error after UTF-8 characters (Arabic)
+echo "\nError after Arabic characters:\n";
+json_validate_trycatchdump('{"اسم": "قيمة}');
+
+// Error after UTF-8 characters (Emoji)
+echo "\nError after Emoji:\n";
+json_validate_trycatchdump('{"emoji": "😀🎉}');
+
+// Mixed ASCII and UTF-8
+echo "\nError in mixed ASCII and UTF-8:\n";
+json_validate_trycatchdump('{"name": "John", "город": "Москва}');
+
+// UTF-8 with escaped sequences
+echo "\nError with UTF-8 escaped sequences:\n";
+json_validate_trycatchdump('{"test": "\u30d7\u30ec\u30b9}');
+
+// Multiple UTF-8 keys with error
+echo "\nError in object with multiple UTF-8 keys:\n";
+json_validate_trycatchdump('{"キー1": "値1", "キー2": "値2}');
+
+// Array with UTF-8 and error
+echo "\nError in array with UTF-8 strings:\n";
+json_validate_trycatchdump('["文字列1", "文字列2", "文字列3}');
+
+// Nested object with UTF-8 and error
+echo "\nError in nested object with UTF-8:\n";
+json_validate_trycatchdump('{"外部": {"内部": "値}');
+
+?>
+--EXPECT--
+Testing error locations with Unicode UTF-8 characters
+
+Error after Japanese characters:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded near location 1:12"
+
+Error after Russian characters:
+bool(false)
+int(3)
+string(71) "Control character error, possibly incorrectly encoded near location 1:9"
+
+Error after Chinese characters:
+bool(false)
+int(3)
+string(71) "Control character error, possibly incorrectly encoded near location 1:8"
+
+Error after Arabic characters:
+bool(false)
+int(3)
+string(71) "Control character error, possibly incorrectly encoded near location 1:9"
+
+Error after Emoji:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded 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"
+
+Error with UTF-8 escaped sequences:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded 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"
+
+Error in array with UTF-8 strings:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded 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"
+
diff --git a/ext/json/tests/json_last_error_msg_error_location_003.phpt b/ext/json/tests/json_last_error_msg_error_location_003.phpt
new file mode 100644
index 00000000000..ec5e6b9b4d6
--- /dev/null
+++ b/ext/json/tests/json_last_error_msg_error_location_003.phpt
@@ -0,0 +1,72 @@
+--TEST--
+json_last_error_msg() - Error location reporting with multi-line JSON
+--FILE--
+<?php
+
+require_once("json_validate_requires.inc");
+
+echo "Testing error locations in multi-line JSON\n\n";
+
+// Error on line 2
+echo "Error on line 2, column 13:\n";
+$json = '{
+ "name": "value
+}';
+json_validate_trycatchdump($json);
+
+// Error on line 3
+echo "\nError on line 3, column 12:\n";
+$json = '{
+ "key1": "value1",
+ "key2" "value2"
+}';
+json_validate_trycatchdump($json);
+
+// Error on line 5 in nested structure
+echo "\nError on line 5, column 26:\n";
+$json = '{
+ "outer": {
+ "inner": {
+ "deep": {
+ "value": "unclosed
+ }
+ }
+ }
+}';
+json_validate_trycatchdump($json);
+
+// Error on last line
+echo "\nError on line 7, column 1:\n";
+$json = '{
+ "key1": "value1",
+ "key2": "value2",
+ "key3": "value3",
+ "key4": "value4",
+ "key5": "value5"
+';
+json_validate_trycatchdump($json);
+
+?>
+--EXPECT--
+Testing error locations in multi-line JSON
+
+Error on line 2, column 13:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded near location 2:13"
+
+Error on line 3, column 12:
+bool(false)
+int(4)
+string(31) "Syntax error near location 3:12"
+
+Error on line 5, column 26:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded near location 5:26"
+
+Error on line 7, column 1:
+bool(false)
+int(4)
+string(30) "Syntax error near location 7:1"
+
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
new file mode 100644
index 00000000000..165449600fb
--- /dev/null
+++ b/ext/json/tests/json_last_error_msg_error_location_004.phpt
@@ -0,0 +1,93 @@
+--TEST--
+json_last_error_msg() - Error location reporting with deeply nested structures
+--FILE--
+<?php
+
+require_once("json_validate_requires.inc");
+
+echo "Testing error locations in deeply nested structures\n\n";
+
+// Error in deeply nested object
+echo "Error in deeply nested object:\n";
+$json = '{"a": {"b": {"c": {"d": {"e": "value}}}}}';
+json_validate_trycatchdump($json);
+
+// Error in deeply nested array
+echo "\nError in deeply nested array:\n";
+$json = '[[[[[1, 2, 3, 4, 5]]]]]';
+json_validate_trycatchdump($json);
+
+// Error in mixed nested structures
+echo "\nError in mixed nested structures:\n";
+$json = '{"arr": [{"obj": [{"key": "val"}]}]}';
+json_validate_trycatchdump($json);
+
+// Error at the end of deep nesting
+echo "\nError at end of deep nesting:\n";
+$json = '{"level1": {"level2": {"level3": {"level4": "value"}}}}';
+json_validate_trycatchdump($json);
+
+// Error in middle of deep nesting
+echo "\nError in middle of deep nesting:\n";
+$json = '{"l1": {"l2": {"l3" {"l4": "val"}}}}';
+json_validate_trycatchdump($json);
+
+// Complex structure with error
+echo "\nError in complex structure:\n";
+$json = '{"users": [{"name": "John", "age": 30}, {"name": "Jane", "age": 25}], "count": 2, "status": "active}';
+json_validate_trycatchdump($json);
+
+// Array of objects with error
+echo "\nError in array of objects:\n";
+$json = '[{"id": 1, "name": "A"}, {"id": 2, "name": "B"}, {"id": 3, "name": "C}]';
+json_validate_trycatchdump($json);
+
+// Object with array values error
+echo "\nError in object with array values:\n";
+$json = '{"numbers": [1, 2, 3], "strings": ["a", "b", "c"], "booleans": [true, false, true}';
+json_validate_trycatchdump($json);
+
+?>
+--EXPECT--
+Testing error locations in deeply nested structures
+
+Error in deeply nested object:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded near location 1:31"
+
+Error in deeply nested array:
+bool(true)
+int(0)
+string(8) "No error"
+
+Error in mixed nested structures:
+bool(true)
+int(0)
+string(8) "No error"
+
+Error at end of deep nesting:
+bool(true)
+int(0)
+string(8) "No error"
+
+Error in middle of deep nesting:
+bool(false)
+int(4)
+string(31) "Syntax error near location 1:21"
+
+Error in complex structure:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded 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"
+
+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
new file mode 100644
index 00000000000..d12ce387e73
--- /dev/null
+++ b/ext/json/tests/json_last_error_msg_error_location_005.phpt
@@ -0,0 +1,103 @@
+--TEST--
+json_last_error_msg() - Error location reporting with UTF-16 surrogate pairs
+--FILE--
+<?php
+
+require_once("json_validate_requires.inc");
+
+echo "Testing error locations with UTF-16 surrogate pairs and escape sequences\n\n";
+
+// Error after UTF-16 escaped characters
+echo "Error after UTF-16 escaped emoji:\n";
+json_validate_trycatchdump('{"emoji": "\uD83D\uDE00}');
+
+// Error after multiple UTF-16 pairs
+echo "\nError after multiple UTF-16 pairs:\n";
+json_validate_trycatchdump('{"test": "\uD83D\uDE00\uD83C\uDF89}');
+
+// Mixed UTF-8 and UTF-16 escapes
+echo "\nError with mixed UTF-8 and UTF-16:\n";
+json_validate_trycatchdump('{"mixed": "Hello \u4E16\u754C world}');
+
+// UTF-16 in key and value
+echo "\nError with UTF-16 in key:\n";
+json_validate_trycatchdump('{"\u30D7\u30EC\u30B9": "value}');
+
+// Multiple keys with UTF-16
+echo "\nError with multiple UTF-16 keys:\n";
+json_validate_trycatchdump('{"\u0041\u0042": "val1", "\u0043\u0044": "val2}');
+
+// BMP characters (Basic Multilingual Plane)
+echo "\nError with BMP characters:\n";
+json_validate_trycatchdump('{"test": "\u0048\u0065\u006C\u006C\u006F}');
+
+// Supplementary plane characters (beyond BMP)
+echo "\nError with supplementary plane:\n";
+json_validate_trycatchdump('{"music": "\uD834\uDD1E}');
+
+// Array with UTF-16 escaped strings
+echo "\nError in array with UTF-16:\n";
+json_validate_trycatchdump('["\u0031", "\u0032", "\u0033}');
+
+// Nested with UTF-16
+echo "\nError in nested structure with UTF-16:\n";
+json_validate_trycatchdump('{"outer": {"\u6D4B\u8BD5": "value}');
+
+// UTF-16 with control characters
+echo "\nError with UTF-16 and control chars:\n";
+json_validate_trycatchdump('{"data": "\u0009\u000A\u000D}');
+
+?>
+--EXPECT--
+Testing error locations with UTF-16 surrogate pairs and escape sequences
+
+Error after UTF-16 escaped emoji:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded 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"
+
+Error with mixed UTF-8 and UTF-16:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded 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"
+
+Error with multiple UTF-16 keys:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded near location 1:22"
+
+Error with BMP characters:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded near location 1:10"
+
+Error with supplementary plane:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded 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"
+
+Error in nested structure with UTF-16:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded 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"
+
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
new file mode 100644
index 00000000000..e6aab1af8f2
--- /dev/null
+++ b/ext/json/tests/json_last_error_msg_error_location_006.phpt
@@ -0,0 +1,152 @@
+--TEST--
+json_last_error_msg() - Error location reporting edge cases
+--FILE--
+<?php
+
+require_once("json_validate_requires.inc");
+
+echo "Testing error location edge cases\n\n";
+
+// Error at very start
+echo "Error at position 1:1:\n";
+json_validate_trycatchdump('');
+
+// Error at position 1:1 with single character
+echo "\nError at position 1:1 with invalid char:\n";
+json_validate_trycatchdump('x');
+
+// Error after whitespace
+echo "\nError after leading whitespace:\n";
+json_validate_trycatchdump(' {');
+
+// Error with only whitespace
+echo "\nError with tabs and spaces:\n";
+json_validate_trycatchdump(" \t \n ");
+
+// Error after newlines
+echo "\nError after multiple newlines:\n";
+json_validate_trycatchdump("\n\n\n{");
+
+// Long string with error at end
+echo "\nError at end of long string:\n";
+$longString = '{"key": "' . str_repeat('a', 1000) . '"}';
+// Remove closing brace to create error
+$longString = substr($longString, 0, -1);
+json_validate_trycatchdump($longString);
+
+// Error with very long key name
+echo "\nError with very long key:\n";
+$longKey = '{"' . str_repeat('x', 500) . '": "value}';
+json_validate_trycatchdump($longKey);
+
+// Empty object followed by error
+echo "\nError after empty object:\n";
+json_validate_trycatchdump('{}x');
+
+// Empty array followed by error
+echo "\nError after empty array:\n";
+json_validate_trycatchdump('[]x');
+
+// Multiple values (invalid JSON)
+echo "\nError with multiple root values:\n";
+json_validate_trycatchdump('{}{}');
+
+// Number followed by invalid content
+echo "\nError after valid number:\n";
+json_validate_trycatchdump('123x');
+
+// Boolean followed by invalid content
+echo "\nError after valid boolean:\n";
+json_validate_trycatchdump('truex');
+
+// Null followed by invalid content
+echo "\nError after valid null:\n";
+json_validate_trycatchdump('nullx');
+
+// String followed by invalid content
+echo "\nError after valid string:\n";
+json_validate_trycatchdump('"test"x');
+
+// Error with mixed whitespace
+echo "\nError with mixed whitespace:\n";
+json_validate_trycatchdump("{\n\t\"key\": \n\t\"value\n}");
+
+?>
+--EXPECT--
+Testing error location edge cases
+
+Error at position 1:1:
+bool(false)
+int(4)
+string(12) "Syntax error"
+
+Error at position 1:1 with invalid char:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:1"
+
+Error after leading whitespace:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:5"
+
+Error with tabs and spaces:
+bool(false)
+int(4)
+string(30) "Syntax error near location 2:3"
+
+Error after multiple newlines:
+bool(false)
+int(4)
+string(30) "Syntax error near location 4:2"
+
+Error at end of long string:
+bool(false)
+int(4)
+string(33) "Syntax error near location 1:1011"
+
+Error with very long key:
+bool(false)
+int(3)
+string(73) "Control character error, possibly incorrectly encoded near location 1:506"
+
+Error after empty object:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:3"
+
+Error after empty array:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:3"
+
+Error with multiple root values:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:3"
+
+Error after valid number:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:4"
+
+Error after valid boolean:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:5"
+
+Error after valid null:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:5"
+
+Error after valid string:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:7"
+
+Error with mixed whitespace:
+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
new file mode 100644
index 00000000000..0e24889bbbb
--- /dev/null
+++ b/ext/json/tests/json_last_error_msg_error_location_007.phpt
@@ -0,0 +1,178 @@
+--TEST--
+json_last_error_msg() - Error location with various error types
+--FILE--
+<?php
+
+require_once("json_validate_requires.inc");
+
+echo "Testing error locations with different error types\n\n";
+
+// State mismatch errors
+echo "State mismatch - expected value:\n";
+json_validate_trycatchdump('{"key": }');
+
+echo "\nState mismatch - expected key:\n";
+json_validate_trycatchdump('{: "value"}');
+
+echo "\nState mismatch - trailing comma in object:\n";
+json_validate_trycatchdump('{"a": 1, "b": 2,}');
+
+echo "\nState mismatch - trailing comma in array:\n";
+json_validate_trycatchdump('[1, 2, 3,]');
+
+// Number format errors
+echo "\nInvalid number format - leading zero:\n";
+json_validate_trycatchdump('{"num": 01}');
+
+echo "\nInvalid number format - multiple decimals:\n";
+json_validate_trycatchdump('{"num": 1.2.3}');
+
+echo "\nInvalid number format - incomplete exponent:\n";
+json_validate_trycatchdump('{"num": 1e}');
+
+echo "\nInvalid number format - double sign:\n";
+json_validate_trycatchdump('{"num": --1}');
+
+// String errors
+echo "\nUnclosed string:\n";
+json_validate_trycatchdump('{"key": "value');
+
+echo "\nInvalid escape sequence:\n";
+json_validate_trycatchdump('{"key": "\\q"}');
+
+echo "\nIncomplete unicode escape:\n";
+json_validate_trycatchdump('{"key": "\\u123"}');
+
+echo "\nInvalid unicode escape:\n";
+json_validate_trycatchdump('{"key": "\\uGGGG"}');
+
+// Keyword errors
+echo "\nInvalid true keyword:\n";
+json_validate_trycatchdump('{"val": tru}');
+
+echo "\nInvalid false keyword:\n";
+json_validate_trycatchdump('{"val": fals}');
+
+echo "\nInvalid null keyword:\n";
+json_validate_trycatchdump('{"val": nul}');
+
+// Structure errors
+echo "\nMismatched brackets - ] instead of }:\n";
+json_validate_trycatchdump('{"key": "val"]');
+
+echo "\nMismatched brackets - } instead of ]:\n";
+json_validate_trycatchdump('["val"}');
+
+echo "\nExtra closing bracket:\n";
+json_validate_trycatchdump('{"key": "val"}}');
+
+echo "\nMissing comma between elements:\n";
+json_validate_trycatchdump('[1 2 3]');
+
+echo "\nMissing comma between object properties:\n";
+json_validate_trycatchdump('{"a": 1 "b": 2}');
+
+?>
+--EXPECT--
+Testing error locations with different error types
+
+State mismatch - expected value:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:9"
+
+State mismatch - expected key:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:2"
+
+State mismatch - trailing comma in object:
+bool(false)
+int(4)
+string(31) "Syntax error near location 1:17"
+
+State mismatch - trailing comma in array:
+bool(false)
+int(4)
+string(31) "Syntax error near location 1:10"
+
+Invalid number format - leading zero:
+bool(false)
+int(4)
+string(31) "Syntax error near location 1:10"
+
+Invalid number format - multiple decimals:
+bool(false)
+int(4)
+string(31) "Syntax error near location 1:12"
+
+Invalid number format - incomplete exponent:
+bool(false)
+int(4)
+string(31) "Syntax error near location 1:10"
+
+Invalid number format - double sign:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:9"
+
+Unclosed string:
+bool(false)
+int(3)
+string(71) "Control character error, possibly incorrectly encoded near location 1:9"
+
+Invalid escape sequence:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:9"
+
+Incomplete unicode escape:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:9"
+
+Invalid unicode escape:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:9"
+
+Invalid true keyword:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:9"
+
+Invalid false keyword:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:9"
+
+Invalid null keyword:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:9"
+
+Mismatched brackets - ] instead of }:
+bool(false)
+int(2)
+string(61) "State mismatch (invalid or malformed JSON) near location 1:14"
+
+Mismatched brackets - } instead of ]:
+bool(false)
+int(2)
+string(60) "State mismatch (invalid or malformed JSON) near location 1:7"
+
+Extra closing bracket:
+bool(false)
+int(4)
+string(31) "Syntax error near location 1:15"
+
+Missing comma between elements:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:4"
+
+Missing comma between object properties:
+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
new file mode 100644
index 00000000000..4d8a1012316
--- /dev/null
+++ b/ext/json/tests/json_last_error_msg_error_location_008.phpt
@@ -0,0 +1,182 @@
+--TEST--
+json_last_error_msg() - Error location with mixed UTF-8 multi-byte characters
+--FILE--
+<?php
+
+require_once("json_validate_requires.inc");
+
+echo "Testing error locations with various UTF-8 multi-byte character widths\n\n";
+
+// 2-byte UTF-8 characters (Latin Extended)
+echo "Error with 2-byte UTF-8 (Latin Extended):\n";
+json_validate_trycatchdump('{"café": "value}');
+
+echo "\nError with 2-byte UTF-8 (Greek):\n";
+json_validate_trycatchdump('{"Ελληνικά": "value}');
+
+echo "\nError with 2-byte UTF-8 (Cyrillic):\n";
+json_validate_trycatchdump('{"Привет": "мир}');
+
+// 3-byte UTF-8 characters (CJK)
+echo "\nError with 3-byte UTF-8 (Chinese):\n";
+json_validate_trycatchdump('{"中文": "测试}');
+
+echo "\nError with 3-byte UTF-8 (Japanese Hiragana):\n";
+json_validate_trycatchdump('{"ひらがな": "テスト}');
+
+echo "\nError with 3-byte UTF-8 (Japanese Katakana):\n";
+json_validate_trycatchdump('{"カタカナ": "テスト}');
+
+echo "\nError with 3-byte UTF-8 (Korean):\n";
+json_validate_trycatchdump('{"한글": "테스트}');
+
+// 4-byte UTF-8 characters (Emoji and special symbols)
+echo "\nError with 4-byte UTF-8 (Emoji faces):\n";
+json_validate_trycatchdump('{"emoji": "😀😃😄😁}');
+
+echo "\nError with 4-byte UTF-8 (Emoji objects):\n";
+json_validate_trycatchdump('{"objects": "🎉🎊🎁🎈}');
+
+echo "\nError with 4-byte UTF-8 (Mathematical symbols):\n";
+json_validate_trycatchdump('{"math": "𝕒𝕓𝕔𝕕}');
+
+// Mixed byte-width characters
+echo "\nError with mixed 1-2-3 byte UTF-8:\n";
+json_validate_trycatchdump('{"mix": "Aéø中文}');
+
+echo "\nError with mixed 2-3-4 byte UTF-8:\n";
+json_validate_trycatchdump('{"mix": "Привет中文😀}');
+
+echo "\nError with all byte widths:\n";
+json_validate_trycatchdump('{"all": "Aéø中文😀}');
+
+// UTF-8 in keys at different positions
+echo "\nError with UTF-8 key at start:\n";
+json_validate_trycatchdump('{"🔑": "value}');
+
+echo "\nError with multiple UTF-8 keys:\n";
+json_validate_trycatchdump('{"キー1": "値1", "キー2": "値2", "キー3": "値3}');
+
+// Array with mixed UTF-8
+echo "\nError in array with mixed UTF-8:\n";
+json_validate_trycatchdump('["ASCII", "café", "中文", "😀}');
+
+// Complex nested structure with UTF-8
+echo "\nError in nested structure with various UTF-8:\n";
+json_validate_trycatchdump('{"外层": {"中层": {"内层": "值}}}');
+
+// UTF-8 with special combining characters
+echo "\nError with combining diacritical marks:\n";
+json_validate_trycatchdump('{"tëst": "vâlue}');
+
+// Right-to-left languages
+echo "\nError with Hebrew:\n";
+json_validate_trycatchdump('{"שלום": "עולם}');
+
+echo "\nError with Arabic with diacritics:\n";
+json_validate_trycatchdump('{"مرحبا": "عالم}');
+
+?>
+--EXPECT--
+Testing error locations with various UTF-8 multi-byte character widths
+
+Error with 2-byte UTF-8 (Latin Extended):
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded 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"
+
+Error with 2-byte UTF-8 (Cyrillic):
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded 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"
+
+Error with 3-byte UTF-8 (Japanese Hiragana):
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded 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"
+
+Error with 3-byte UTF-8 (Korean):
+bool(false)
+int(3)
+string(71) "Control character error, possibly incorrectly encoded 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"
+
+Error with 4-byte UTF-8 (Emoji objects):
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded 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"
+
+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"
+
+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"
+
+Error with all byte widths:
+bool(false)
+int(3)
+string(71) "Control character error, possibly incorrectly encoded 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"
+
+Error with multiple UTF-8 keys:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded 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"
+
+Error in nested structure with various UTF-8:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded 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"
+
+Error with Hebrew:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded 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"
+
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
new file mode 100644
index 00000000000..406179693ef
--- /dev/null
+++ b/ext/json/tests/json_last_error_msg_error_location_009.phpt
@@ -0,0 +1,110 @@
+--TEST--
+json_last_error_msg() - Error location with depth errors and complex nesting
+--FILE--
+<?php
+
+require_once("json_validate_requires.inc");
+
+echo "Testing error locations with depth-related issues\n\n";
+
+// Test max depth error with location
+echo "Max depth error at specific location:\n";
+$json = '{"a":{"b":{"c":{"d":{"e":{"f":{"g":{"h":{"i":{"j":"val"}}}}}}}}}}';
+json_validate_trycatchdump($json, 5);
+
+echo "\nMax depth error in array:\n";
+$json = '[[[[[[[[[[10]]]]]]]]]]';
+json_validate_trycatchdump($json, 5);
+
+echo "\nMax depth error with mixed structures:\n";
+$json = '[{"a":[{"b":[{"c":"val"}]}]}]';
+json_validate_trycatchdump($json, 3);
+
+// Syntax error at deep level
+echo "\nSyntax error at deep nesting level:\n";
+$json = '{"l1":{"l2":{"l3":{"l4":{"l5":"unclosed}}}}}';
+json_validate_trycatchdump($json);
+
+echo "\nSyntax error in deep array:\n";
+$json = '[[[[["value]]]]]';
+json_validate_trycatchdump($json);
+
+// Valid deep structure followed by error
+echo "\nError after valid deep structure:\n";
+$json = '{"valid":{"nested":{"structure":"ok"}},"error":"unclosed}';
+json_validate_trycatchdump($json);
+
+// Multiple nested objects with error in middle
+echo "\nError in middle of nested structure:\n";
+$json = '{"outer":{"middle":{"inner" "value"}}}';
+json_validate_trycatchdump($json);
+
+// Array with nested objects and error
+echo "\nError in array with nested objects:\n";
+$json = '[{"a":{"b":"val"}},{"c":{"d":"val}}}]';
+json_validate_trycatchdump($json);
+
+// Deep structure with UTF-8 and error
+echo "\nError in deep UTF-8 structure:\n";
+$json = '{"外":{"中":{"内":"値}}}';
+json_validate_trycatchdump($json);
+
+// Very deep valid structure (should pass)
+echo "\nValid deep structure within limit:\n";
+$json = '{"a":{"b":{"c":{"d":"val"}}}}';
+json_validate_trycatchdump($json, 10);
+
+?>
+--EXPECT--
+Testing error locations with depth-related issues
+
+Max depth error at specific location:
+bool(false)
+int(1)
+string(47) "Maximum stack depth exceeded near location 1:21"
+
+Max depth error in array:
+bool(false)
+int(1)
+string(46) "Maximum stack depth exceeded near location 1:5"
+
+Max depth error with mixed structures:
+bool(false)
+int(1)
+string(46) "Maximum stack depth exceeded near location 1:7"
+
+Syntax error at deep nesting level:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded 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"
+
+Error after valid deep structure:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded near location 1:48"
+
+Error in middle of nested structure:
+bool(false)
+int(4)
+string(31) "Syntax error near location 1:29"
+
+Error in array with nested objects:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded 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"
+
+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
new file mode 100644
index 00000000000..10857020583
--- /dev/null
+++ b/ext/json/tests/json_last_error_msg_error_location_010.phpt
@@ -0,0 +1,164 @@
+--TEST--
+json_last_error_msg() - Error location with whitespace variations
+--FILE--
+<?php
+
+require_once("json_validate_requires.inc");
+
+echo "Testing error locations with various whitespace patterns\n\n";
+
+// Error after spaces
+echo "Error after multiple spaces:\n";
+json_validate_trycatchdump(' {');
+
+// Error after tabs
+echo "\nError after tabs:\n";
+json_validate_trycatchdump("\t\t\t{");
+
+// Error after mixed whitespace
+echo "\nError after mixed whitespace:\n";
+json_validate_trycatchdump(" \t {");
+
+// Error on new line
+echo "\nError on second line:\n";
+json_validate_trycatchdump("{\n}x");
+
+// Error with CRLF line endings
+echo "\nError with CRLF line endings:\n";
+json_validate_trycatchdump("{\r\n\"key\": \"value\r\n}");
+
+// Error with multiple blank lines
+echo "\nError after blank lines:\n";
+json_validate_trycatchdump("\n\n\n{");
+
+// Whitespace inside strings (should not affect location)
+echo "\nError in string with spaces:\n";
+json_validate_trycatchdump('{"key": "value with spaces}');
+
+// Whitespace before and after colons
+echo "\nError with whitespace around colon:\n";
+json_validate_trycatchdump('{"key" : "value}');
+
+// Whitespace before and after commas
+echo "\nError with whitespace around comma:\n";
+json_validate_trycatchdump('{"a": 1 , "b": 2 , "c": 3}');
+
+// Pretty printed JSON with error
+echo "\nError in pretty printed JSON:\n";
+$json = '{
+ "name": "John",
+ "age": 30,
+ "city": "New York
+}';
+json_validate_trycatchdump($json);
+
+// Heavily indented JSON with error
+echo "\nError in heavily indented JSON:\n";
+$json = '{
+ "level1": {
+ "level2": {
+ "level3": {
+ "value": "test
+ }
+ }
+ }
+}';
+json_validate_trycatchdump($json);
+
+// Mixed tabs and spaces with error
+echo "\nError with mixed tabs and spaces:\n";
+$json = "{\n\t \"key1\": \"val1\",\n \t\"key2\": \"val2\n}";
+json_validate_trycatchdump($json);
+
+// Whitespace-only line before error
+echo "\nError after whitespace-only line:\n";
+$json = "{\n \n \"key\": \"value\n}";
+json_validate_trycatchdump($json);
+
+// No whitespace (compact JSON) with error
+echo "\nError in compact JSON:\n";
+json_validate_trycatchdump('{"a":"b","c":"d","e":"f}');
+
+// Unicode whitespace characters (if supported)
+echo "\nError with regular spaces:\n";
+json_validate_trycatchdump('{ "key": "value}');
+
+?>
+--EXPECT--
+Testing error locations with various whitespace patterns
+
+Error after multiple spaces:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:7"
+
+Error after tabs:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:5"
+
+Error after mixed whitespace:
+bool(false)
+int(4)
+string(30) "Syntax error near location 1:7"
+
+Error on second line:
+bool(false)
+int(4)
+string(30) "Syntax error near location 2:2"
+
+Error with CRLF line endings:
+bool(false)
+int(3)
+string(71) "Control character error, possibly incorrectly encoded near location 2:8"
+
+Error after blank lines:
+bool(false)
+int(4)
+string(30) "Syntax error near location 4:2"
+
+Error in string with spaces:
+bool(false)
+int(3)
+string(71) "Control character error, possibly incorrectly encoded 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"
+
+Error with whitespace around comma:
+bool(true)
+int(0)
+string(8) "No error"
+
+Error in pretty printed JSON:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded near location 4:11"
+
+Error in heavily indented JSON:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded near location 5:26"
+
+Error with mixed tabs and spaces:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded near location 3:12"
+
+Error after whitespace-only line:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded near location 3:12"
+
+Error in compact JSON:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded near location 1:22"
+
+Error with regular spaces:
+bool(false)
+int(3)
+string(72) "Control character error, possibly incorrectly encoded near location 1:10"
+
diff --git a/ext/json/tests/json_validate_002.phpt b/ext/json/tests/json_validate_002.phpt
index 53f4e4f2c2e..423564c4ad7 100644
--- a/ext/json/tests/json_validate_002.phpt
+++ b/ext/json/tests/json_validate_002.phpt
@@ -23,13 +23,13 @@
string(12) "Syntax error"
bool(false)
int(4)
-string(12) "Syntax error"
+string(30) "Syntax error near location 1:1"
bool(false)
int(4)
string(12) "Syntax error"
bool(false)
int(1)
-string(28) "Maximum stack depth exceeded"
+string(46) "Maximum stack depth exceeded near location 1:1"
bool(true)
int(0)
string(8) "No error"
@@ -44,7 +44,7 @@
string(8) "No error"
bool(false)
int(4)
-string(12) "Syntax error"
+string(30) "Syntax error near location 1:1"
bool(true)
int(0)
string(8) "No error"
diff --git a/ext/json/tests/json_validate_004.phpt b/ext/json/tests/json_validate_004.phpt
index d8a798d9432..bd807defa14 100644
--- a/ext/json/tests/json_validate_004.phpt
+++ b/ext/json/tests/json_validate_004.phpt
@@ -23,16 +23,16 @@
Testing Invalid UTF-8
bool(false)
int(5)
-string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
+string(74) "Malformed UTF-8 characters, possibly incorrectly encoded near location 1:1"
bool(false)
int(5)
-string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
+string(74) "Malformed UTF-8 characters, possibly incorrectly encoded near location 1:1"
bool(false)
int(5)
-string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
+string(74) "Malformed UTF-8 characters, possibly incorrectly encoded near location 1:1"
bool(false)
int(5)
-string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
+string(74) "Malformed UTF-8 characters, possibly incorrectly encoded near location 1:2"
bool(true)
int(0)
string(8) "No error"