Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ext/json: Refactor php_json_encode_serializable_object() to call method directly #17460

Merged
merged 2 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ PHP 8.5 INTERNALS UPGRADE NOTES
. The gdImageScale*() and gdImageRotate*() helpers are now internal in the
bundled libgd, like they have been in external libgd as of gd-2.1.1.

- ext/json
. php_json_encode_serializable_object() now assumes `EG(active)`,
if not a bailout is caused. Therefore a minor BC break exists if the
`PHP_JSON_PARTIAL_OUTPUT_ON_ERROR` option is in use.
However, this situation is highly unlikely.

- ext/libxml
. The refcount APIs now return an `unsigned int` instead of an `int`.

Expand Down
41 changes: 11 additions & 30 deletions ext/json/json_encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -579,12 +579,11 @@ zend_result php_json_escape_string(
}
/* }}} */

static zend_result php_json_encode_serializable_object(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
static zend_result php_json_encode_serializable_object(smart_str *buf, zend_object *obj, int options, php_json_encoder *encoder)
{
zend_class_entry *ce = Z_OBJCE_P(val);
zend_object *obj = Z_OBJ_P(val);
zend_class_entry *ce = obj->ce;
uint32_t *guard = zend_get_recursion_guard(obj);
zval retval, fname;
zval retval;
zend_result return_code;

ZEND_ASSERT(guard != NULL);
Expand All @@ -599,35 +598,19 @@ static zend_result php_json_encode_serializable_object(smart_str *buf, zval *val

ZEND_GUARD_PROTECT_RECURSION(guard, JSON);

ZVAL_STRING(&fname, "jsonSerialize");

if (FAILURE == call_user_function(NULL, val, &fname, &retval, 0, NULL) || Z_TYPE(retval) == IS_UNDEF) {
Girgias marked this conversation as resolved.
Show resolved Hide resolved
if (!EG(exception)) {
zend_throw_exception_ex(NULL, 0, "Failed calling %s::jsonSerialize()", ZSTR_VAL(ce->name));
}
zval_ptr_dtor(&fname);

zend_function *json_serialize_method = zend_hash_str_find_ptr(&ce->function_table, ZEND_STRL("jsonserialize"));
ZEND_ASSERT(json_serialize_method != NULL && "This should be guaranteed prior to calling this function");
zend_call_known_function(json_serialize_method, obj, ce, &retval, 0, NULL, NULL);
/* An exception has occurred */
if (Z_TYPE(retval) == IS_UNDEF) {
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
smart_str_appendl(buf, "null", 4);
}
ZEND_GUARD_UNPROTECT_RECURSION(guard, JSON);
return FAILURE;
}

if (EG(exception)) {
/* Error already raised */
zval_ptr_dtor(&retval);
zval_ptr_dtor(&fname);

if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
smart_str_appendl(buf, "null", 4);
}
ZEND_GUARD_UNPROTECT_RECURSION(guard, JSON);
return FAILURE;
}

if ((Z_TYPE(retval) == IS_OBJECT) &&
(Z_OBJ(retval) == Z_OBJ_P(val))) {
if (Z_TYPE(retval) == IS_OBJECT && Z_OBJ(retval) == obj) {
/* Handle the case where jsonSerialize does: return $this; by going straight to encode array */
ZEND_GUARD_UNPROTECT_RECURSION(guard, JSON);
return_code = php_json_encode_array(buf, &retval, options, encoder);
Expand All @@ -638,11 +621,9 @@ static zend_result php_json_encode_serializable_object(smart_str *buf, zval *val
}

zval_ptr_dtor(&retval);
zval_ptr_dtor(&fname);

return return_code;
}
/* }}} */

static zend_result php_json_encode_serializable_enum(smart_str *buf, zval *val, int options, php_json_encoder *encoder)
{
Expand Down Expand Up @@ -691,7 +672,7 @@ zend_result php_json_encode_zval(smart_str *buf, zval *val, int options, php_jso

case IS_OBJECT:
if (instanceof_function(Z_OBJCE_P(val), php_json_serializable_ce)) {
return php_json_encode_serializable_object(buf, val, options, encoder);
return php_json_encode_serializable_object(buf, Z_OBJ_P(val), options, encoder);
}
if (Z_OBJCE_P(val)->ce_flags & ZEND_ACC_ENUM) {
return php_json_encode_serializable_enum(buf, val, options, encoder);
Expand All @@ -702,7 +683,7 @@ zend_result php_json_encode_zval(smart_str *buf, zval *val, int options, php_jso
/* Avoid modifications (and potential freeing) of the array through a reference when a
* jsonSerialize() method is invoked. */
zval zv;
int res;
zend_result res;
ZVAL_COPY(&zv, val);
res = php_json_encode_array(buf, &zv, options, encoder);
zval_ptr_dtor_nogc(&zv);
Expand Down
Loading