mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
Merge JSON_THROW_ON_ERROR
This commit is contained in:
parent
d5127cec6a
commit
e823770515
7 changed files with 236 additions and 34 deletions
3
NEWS
3
NEWS
|
@ -39,6 +39,9 @@ PHP NEWS
|
||||||
. Fixed bug #75317 (UConverter::setDestinationEncoding changes source instead
|
. Fixed bug #75317 (UConverter::setDestinationEncoding changes source instead
|
||||||
of destination). (andrewnester)
|
of destination). (andrewnester)
|
||||||
|
|
||||||
|
- JSON:
|
||||||
|
. Added JSON_THROW_ON_ERROR flag. (Andrea)
|
||||||
|
|
||||||
- LDAP:
|
- LDAP:
|
||||||
. Added ldap_exop_refresh helper for EXOP REFRESH operation with dds overlay.
|
. Added ldap_exop_refresh helper for EXOP REFRESH operation with dds overlay.
|
||||||
(Come)
|
(Come)
|
||||||
|
|
15
UPGRADING
15
UPGRADING
|
@ -55,6 +55,15 @@ BCMath:
|
||||||
5. Changed Functions
|
5. Changed Functions
|
||||||
========================================
|
========================================
|
||||||
|
|
||||||
|
JSON:
|
||||||
|
. A new flag has been added, JSON_THROW_ON_ERROR, which can be used with
|
||||||
|
json_decode() or json_encode() and causes these functions to throw a
|
||||||
|
JsonException upon an error, instead of setting the global error state that
|
||||||
|
is retrieved with json_last_error(). JSON_PARTIAL_OUTPUT_ON_ERROR takes
|
||||||
|
precedence over JSON_THROW_ON_ERROR.
|
||||||
|
(RFC: https://wiki.php.net/rfc/json_throw_on_error)
|
||||||
|
|
||||||
|
Standard:
|
||||||
. debug_zval_dump() was changed to display recursive arrays and objects
|
. debug_zval_dump() was changed to display recursive arrays and objects
|
||||||
in the same way as var_dump(). Now, it doesn't display them twice.
|
in the same way as var_dump(). Now, it doesn't display them twice.
|
||||||
|
|
||||||
|
@ -70,6 +79,9 @@ Date:
|
||||||
7. New Classes and Interfaces
|
7. New Classes and Interfaces
|
||||||
========================================
|
========================================
|
||||||
|
|
||||||
|
JSON:
|
||||||
|
. JsonException
|
||||||
|
|
||||||
========================================
|
========================================
|
||||||
8. Removed Extensions and SAPIs
|
8. Removed Extensions and SAPIs
|
||||||
========================================
|
========================================
|
||||||
|
@ -93,6 +105,9 @@ Date:
|
||||||
10. New Global Constants
|
10. New Global Constants
|
||||||
========================================
|
========================================
|
||||||
|
|
||||||
|
JSON:
|
||||||
|
. JSON_THROW_ON_ERROR
|
||||||
|
|
||||||
PGSQL:
|
PGSQL:
|
||||||
. Requires Postgres 9.3
|
. Requires Postgres 9.3
|
||||||
- PGSQL_DIAG_SCHEMA_NAME
|
- PGSQL_DIAG_SCHEMA_NAME
|
||||||
|
|
|
@ -38,6 +38,7 @@ static PHP_FUNCTION(json_last_error);
|
||||||
static PHP_FUNCTION(json_last_error_msg);
|
static PHP_FUNCTION(json_last_error_msg);
|
||||||
|
|
||||||
PHP_JSON_API zend_class_entry *php_json_serializable_ce;
|
PHP_JSON_API zend_class_entry *php_json_serializable_ce;
|
||||||
|
PHP_JSON_API zend_class_entry *php_json_exception_ce;
|
||||||
|
|
||||||
PHP_JSON_API ZEND_DECLARE_MODULE_GLOBALS(json)
|
PHP_JSON_API ZEND_DECLARE_MODULE_GLOBALS(json)
|
||||||
|
|
||||||
|
@ -95,6 +96,9 @@ static PHP_MINIT_FUNCTION(json)
|
||||||
INIT_CLASS_ENTRY(ce, "JsonSerializable", json_serializable_interface);
|
INIT_CLASS_ENTRY(ce, "JsonSerializable", json_serializable_interface);
|
||||||
php_json_serializable_ce = zend_register_internal_interface(&ce);
|
php_json_serializable_ce = zend_register_internal_interface(&ce);
|
||||||
|
|
||||||
|
INIT_CLASS_ENTRY(ce, "JsonException", NULL);
|
||||||
|
php_json_exception_ce = zend_register_internal_class_ex(&ce, zend_ce_exception);
|
||||||
|
|
||||||
/* options for json_encode */
|
/* options for json_encode */
|
||||||
PHP_JSON_REGISTER_CONSTANT("JSON_HEX_TAG", PHP_JSON_HEX_TAG);
|
PHP_JSON_REGISTER_CONSTANT("JSON_HEX_TAG", PHP_JSON_HEX_TAG);
|
||||||
PHP_JSON_REGISTER_CONSTANT("JSON_HEX_AMP", PHP_JSON_HEX_AMP);
|
PHP_JSON_REGISTER_CONSTANT("JSON_HEX_AMP", PHP_JSON_HEX_AMP);
|
||||||
|
@ -116,6 +120,7 @@ static PHP_MINIT_FUNCTION(json)
|
||||||
/* common options for json_decode and json_encode */
|
/* common options for json_decode and json_encode */
|
||||||
PHP_JSON_REGISTER_CONSTANT("JSON_INVALID_UTF8_IGNORE", PHP_JSON_INVALID_UTF8_IGNORE);
|
PHP_JSON_REGISTER_CONSTANT("JSON_INVALID_UTF8_IGNORE", PHP_JSON_INVALID_UTF8_IGNORE);
|
||||||
PHP_JSON_REGISTER_CONSTANT("JSON_INVALID_UTF8_SUBSTITUTE", PHP_JSON_INVALID_UTF8_SUBSTITUTE);
|
PHP_JSON_REGISTER_CONSTANT("JSON_INVALID_UTF8_SUBSTITUTE", PHP_JSON_INVALID_UTF8_SUBSTITUTE);
|
||||||
|
PHP_JSON_REGISTER_CONSTANT("JSON_THROW_ON_ERROR", PHP_JSON_THROW_ON_ERROR);
|
||||||
|
|
||||||
/* json error constants */
|
/* json error constants */
|
||||||
PHP_JSON_REGISTER_CONSTANT("JSON_ERROR_NONE", PHP_JSON_ERROR_NONE);
|
PHP_JSON_REGISTER_CONSTANT("JSON_ERROR_NONE", PHP_JSON_ERROR_NONE);
|
||||||
|
@ -207,6 +212,37 @@ PHP_JSON_API int php_json_encode(smart_str *buf, zval *val, int options) /* {{{
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
static const char *php_json_get_error_msg(php_json_error_code error_code) /* {{{ */
|
||||||
|
{
|
||||||
|
switch(error_code) {
|
||||||
|
case PHP_JSON_ERROR_NONE:
|
||||||
|
return "No error";
|
||||||
|
case PHP_JSON_ERROR_DEPTH:
|
||||||
|
return "Maximum stack depth exceeded";
|
||||||
|
case PHP_JSON_ERROR_STATE_MISMATCH:
|
||||||
|
return "State mismatch (invalid or malformed JSON)";
|
||||||
|
case PHP_JSON_ERROR_CTRL_CHAR:
|
||||||
|
return "Control character error, possibly incorrectly encoded";
|
||||||
|
case PHP_JSON_ERROR_SYNTAX:
|
||||||
|
return "Syntax error";
|
||||||
|
case PHP_JSON_ERROR_UTF8:
|
||||||
|
return "Malformed UTF-8 characters, possibly incorrectly encoded";
|
||||||
|
case PHP_JSON_ERROR_RECURSION:
|
||||||
|
return "Recursion detected";
|
||||||
|
case PHP_JSON_ERROR_INF_OR_NAN:
|
||||||
|
return "Inf and NaN cannot be JSON encoded";
|
||||||
|
case PHP_JSON_ERROR_UNSUPPORTED_TYPE:
|
||||||
|
return "Type is not supported";
|
||||||
|
case PHP_JSON_ERROR_INVALID_PROPERTY_NAME:
|
||||||
|
return "The decoded property name is invalid";
|
||||||
|
case PHP_JSON_ERROR_UTF16:
|
||||||
|
return "Single unpaired UTF-16 surrogate in unicode escape";
|
||||||
|
default:
|
||||||
|
return "Unknown error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
PHP_JSON_API int php_json_decode_ex(zval *return_value, char *str, size_t str_len, zend_long options, zend_long depth) /* {{{ */
|
PHP_JSON_API int php_json_decode_ex(zval *return_value, char *str, size_t str_len, zend_long options, zend_long depth) /* {{{ */
|
||||||
{
|
{
|
||||||
php_json_parser parser;
|
php_json_parser parser;
|
||||||
|
@ -214,7 +250,12 @@ PHP_JSON_API int php_json_decode_ex(zval *return_value, char *str, size_t str_le
|
||||||
php_json_parser_init(&parser, return_value, str, str_len, (int)options, (int)depth);
|
php_json_parser_init(&parser, return_value, str, str_len, (int)options, (int)depth);
|
||||||
|
|
||||||
if (php_json_yyparse(&parser)) {
|
if (php_json_yyparse(&parser)) {
|
||||||
JSON_G(error_code) = php_json_parser_error_code(&parser);
|
php_json_error_code error_code = php_json_parser_error_code(&parser);
|
||||||
|
if (!(options & PHP_JSON_THROW_ON_ERROR)) {
|
||||||
|
JSON_G(error_code) = error_code;
|
||||||
|
} else {
|
||||||
|
zend_throw_exception(php_json_exception_ce, php_json_get_error_msg(error_code), error_code);
|
||||||
|
}
|
||||||
RETVAL_NULL();
|
RETVAL_NULL();
|
||||||
return FAILURE;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -243,11 +284,19 @@ static PHP_FUNCTION(json_encode)
|
||||||
php_json_encode_init(&encoder);
|
php_json_encode_init(&encoder);
|
||||||
encoder.max_depth = (int)depth;
|
encoder.max_depth = (int)depth;
|
||||||
php_json_encode_zval(&buf, parameter, (int)options, &encoder);
|
php_json_encode_zval(&buf, parameter, (int)options, &encoder);
|
||||||
JSON_G(error_code) = encoder.error_code;
|
|
||||||
|
|
||||||
if (encoder.error_code != PHP_JSON_ERROR_NONE && !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
|
if (!(options & PHP_JSON_THROW_ON_ERROR) || (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
|
||||||
smart_str_free(&buf);
|
JSON_G(error_code) = encoder.error_code;
|
||||||
RETURN_FALSE;
|
if (encoder.error_code != PHP_JSON_ERROR_NONE && !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
|
||||||
|
smart_str_free(&buf);
|
||||||
|
RETURN_FALSE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (encoder.error_code != PHP_JSON_ERROR_NONE) {
|
||||||
|
smart_str_free(&buf);
|
||||||
|
zend_throw_exception(php_json_exception_ce, php_json_get_error_msg(encoder.error_code), encoder.error_code);
|
||||||
|
RETURN_FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
smart_str_0(&buf); /* copy? */
|
smart_str_0(&buf); /* copy? */
|
||||||
|
@ -277,10 +326,16 @@ static PHP_FUNCTION(json_decode)
|
||||||
Z_PARAM_LONG(options)
|
Z_PARAM_LONG(options)
|
||||||
ZEND_PARSE_PARAMETERS_END();
|
ZEND_PARSE_PARAMETERS_END();
|
||||||
|
|
||||||
JSON_G(error_code) = PHP_JSON_ERROR_NONE;
|
if (!(options & PHP_JSON_THROW_ON_ERROR)) {
|
||||||
|
JSON_G(error_code) = PHP_JSON_ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
if (!str_len) {
|
if (!str_len) {
|
||||||
JSON_G(error_code) = PHP_JSON_ERROR_SYNTAX;
|
if (!(options & PHP_JSON_THROW_ON_ERROR)) {
|
||||||
|
JSON_G(error_code) = PHP_JSON_ERROR_SYNTAX;
|
||||||
|
} else {
|
||||||
|
zend_throw_exception(php_json_exception_ce, php_json_get_error_msg(PHP_JSON_ERROR_SYNTAX), PHP_JSON_ERROR_SYNTAX);
|
||||||
|
}
|
||||||
RETURN_NULL();
|
RETURN_NULL();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,33 +382,7 @@ static PHP_FUNCTION(json_last_error_msg)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(JSON_G(error_code)) {
|
RETURN_STRING(php_json_get_error_msg(JSON_G(error_code)));
|
||||||
case PHP_JSON_ERROR_NONE:
|
|
||||||
RETURN_STRING("No error");
|
|
||||||
case PHP_JSON_ERROR_DEPTH:
|
|
||||||
RETURN_STRING("Maximum stack depth exceeded");
|
|
||||||
case PHP_JSON_ERROR_STATE_MISMATCH:
|
|
||||||
RETURN_STRING("State mismatch (invalid or malformed JSON)");
|
|
||||||
case PHP_JSON_ERROR_CTRL_CHAR:
|
|
||||||
RETURN_STRING("Control character error, possibly incorrectly encoded");
|
|
||||||
case PHP_JSON_ERROR_SYNTAX:
|
|
||||||
RETURN_STRING("Syntax error");
|
|
||||||
case PHP_JSON_ERROR_UTF8:
|
|
||||||
RETURN_STRING("Malformed UTF-8 characters, possibly incorrectly encoded");
|
|
||||||
case PHP_JSON_ERROR_RECURSION:
|
|
||||||
RETURN_STRING("Recursion detected");
|
|
||||||
case PHP_JSON_ERROR_INF_OR_NAN:
|
|
||||||
RETURN_STRING("Inf and NaN cannot be JSON encoded");
|
|
||||||
case PHP_JSON_ERROR_UNSUPPORTED_TYPE:
|
|
||||||
RETURN_STRING("Type is not supported");
|
|
||||||
case PHP_JSON_ERROR_INVALID_PROPERTY_NAME:
|
|
||||||
RETURN_STRING("The decoded property name is invalid");
|
|
||||||
case PHP_JSON_ERROR_UTF16:
|
|
||||||
RETURN_STRING("Single unpaired UTF-16 surrogate in unicode escape");
|
|
||||||
default:
|
|
||||||
RETURN_STRING("Unknown error");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,7 @@ typedef enum {
|
||||||
/* json_decode() and json_encode() common options */
|
/* json_decode() and json_encode() common options */
|
||||||
#define PHP_JSON_INVALID_UTF8_IGNORE (1<<20)
|
#define PHP_JSON_INVALID_UTF8_IGNORE (1<<20)
|
||||||
#define PHP_JSON_INVALID_UTF8_SUBSTITUTE (1<<21)
|
#define PHP_JSON_INVALID_UTF8_SUBSTITUTE (1<<21)
|
||||||
|
#define PHP_JSON_THROW_ON_ERROR (1<<22)
|
||||||
|
|
||||||
/* Internal flags */
|
/* Internal flags */
|
||||||
#define PHP_JSON_OUTPUT_ARRAY 0
|
#define PHP_JSON_OUTPUT_ARRAY 0
|
||||||
|
|
50
ext/json/tests/json_decode_exceptions.phpt
Normal file
50
ext/json/tests/json_decode_exceptions.phpt
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
--TEST--
|
||||||
|
Test json_decode() function : JSON_THROW_ON_ERROR flag
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(json_decode("{", false, 512, JSON_THROW_ON_ERROR));
|
||||||
|
} catch (JsonException $e) {
|
||||||
|
var_dump($e);
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
object(JsonException)#1 (7) {
|
||||||
|
["message":protected]=>
|
||||||
|
string(12) "Syntax error"
|
||||||
|
["string":"Exception":private]=>
|
||||||
|
string(0) ""
|
||||||
|
["code":protected]=>
|
||||||
|
int(4)
|
||||||
|
["file":protected]=>
|
||||||
|
string(%d) "%s"
|
||||||
|
["line":protected]=>
|
||||||
|
int(%d)
|
||||||
|
["trace":"Exception":private]=>
|
||||||
|
array(1) {
|
||||||
|
[0]=>
|
||||||
|
array(4) {
|
||||||
|
["file"]=>
|
||||||
|
string(%d) "%s"
|
||||||
|
["line"]=>
|
||||||
|
int(%d)
|
||||||
|
["function"]=>
|
||||||
|
string(11) "json_decode"
|
||||||
|
["args"]=>
|
||||||
|
array(4) {
|
||||||
|
[0]=>
|
||||||
|
string(1) "{"
|
||||||
|
[1]=>
|
||||||
|
bool(false)
|
||||||
|
[2]=>
|
||||||
|
int(512)
|
||||||
|
[3]=>
|
||||||
|
int(4194304)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
["previous":"Exception":private]=>
|
||||||
|
NULL
|
||||||
|
}
|
56
ext/json/tests/json_encode_exceptions.phpt
Normal file
56
ext/json/tests/json_encode_exceptions.phpt
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
--TEST--
|
||||||
|
Test json_encode() function : JSON_THROW_ON_ERROR flag
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(json_encode("\x80", JSON_THROW_ON_ERROR));
|
||||||
|
} catch (JsonException $e) {
|
||||||
|
var_dump($e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON_PARTIAL_OUTPUT_ON_ERROR is incompatible with exceptions
|
||||||
|
// So it overrides it for the sake of working with wrappers that add the
|
||||||
|
// JSON_THROW_ON_ERROR flag
|
||||||
|
var_dump(json_encode("\x80", JSON_THROW_ON_ERROR | JSON_PARTIAL_OUTPUT_ON_ERROR));
|
||||||
|
var_dump(json_last_error());
|
||||||
|
var_dump(json_last_error_msg());
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
object(JsonException)#1 (7) {
|
||||||
|
["message":protected]=>
|
||||||
|
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
|
||||||
|
["string":"Exception":private]=>
|
||||||
|
string(0) ""
|
||||||
|
["code":protected]=>
|
||||||
|
int(5)
|
||||||
|
["file":protected]=>
|
||||||
|
string(%d) "%s"
|
||||||
|
["line":protected]=>
|
||||||
|
int(%d)
|
||||||
|
["trace":"Exception":private]=>
|
||||||
|
array(1) {
|
||||||
|
[0]=>
|
||||||
|
array(4) {
|
||||||
|
["file"]=>
|
||||||
|
string(%d) "%s"
|
||||||
|
["line"]=>
|
||||||
|
int(%d)
|
||||||
|
["function"]=>
|
||||||
|
string(11) "json_encode"
|
||||||
|
["args"]=>
|
||||||
|
array(2) {
|
||||||
|
[0]=>
|
||||||
|
string(1) "%s"
|
||||||
|
[1]=>
|
||||||
|
int(4194304)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
["previous":"Exception":private]=>
|
||||||
|
NULL
|
||||||
|
}
|
||||||
|
string(4) "null"
|
||||||
|
int(5)
|
||||||
|
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
|
48
ext/json/tests/json_exceptions_error_clearing.phpt
Normal file
48
ext/json/tests/json_exceptions_error_clearing.phpt
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
--TEST--
|
||||||
|
JSON_THROW_ON_ERROR: global error flag untouched
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
var_dump(json_last_error());
|
||||||
|
|
||||||
|
// here we cause a different kind of error to the following errors, so that
|
||||||
|
// we can be sure the global error state looking unchanged isn't coincidence
|
||||||
|
json_decode("\xFF");
|
||||||
|
|
||||||
|
var_dump(json_last_error());
|
||||||
|
|
||||||
|
try {
|
||||||
|
json_decode("", false, 512, JSON_THROW_ON_ERROR);
|
||||||
|
} catch (JsonException $e) {
|
||||||
|
echo "Caught JSON exception: ", $e->getCode(), PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(json_last_error());
|
||||||
|
|
||||||
|
try {
|
||||||
|
json_decode("{", false, 512, JSON_THROW_ON_ERROR);
|
||||||
|
} catch (JsonException $e) {
|
||||||
|
echo "Caught JSON exception: ", $e->getCode(), PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(json_last_error());
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
json_encode(NAN, JSON_THROW_ON_ERROR);
|
||||||
|
} catch (JsonException $e) {
|
||||||
|
echo "Caught JSON exception: ", $e->getCode(), PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(json_last_error());
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
int(0)
|
||||||
|
int(5)
|
||||||
|
Caught JSON exception: 4
|
||||||
|
int(5)
|
||||||
|
Caught JSON exception: 4
|
||||||
|
int(5)
|
||||||
|
Caught JSON exception: 7
|
||||||
|
int(5)
|
Loading…
Add table
Add a link
Reference in a new issue