From 0765e9210cabf4df7e7fd7076798274ba6f03df5 Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Mon, 10 Oct 2022 09:39:58 -0400 Subject: [PATCH] Don't build prop table during json_encode(JsonSerializable) Save memory and time by not building/updating the property table. When performing infinite recursion detection on objects, always check it on the object rather than its property table. This reduces the additional memory usage for both internal and userland implementations of JsonSerializable caused by json_encode. It does this by avoiding creating the properties table for the first time. (Classes such as SplFixedArray both implement JsonSerializable and override get_properties) This change also makes the method of infinite recursion detection consistent with the special case for standard classes from f9f8c1c79cac1b03279190e0c5513a51881615f9 --- ext/json/json_encoder.c | 14 +++++++------- ext/json/tests/bug77843.phpt | 5 +---- .../get_object_vars_variation_004.phpt | 3 +-- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/ext/json/json_encoder.c b/ext/json/json_encoder.c index adb53598326bd..d93e2a385d6fc 100644 --- a/ext/json/json_encoder.c +++ b/ext/json/json_encoder.c @@ -534,12 +534,12 @@ static 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) /* {{{ */ { + zend_object *obj = Z_OBJ_P(val); zend_class_entry *ce = Z_OBJCE_P(val); - HashTable* myht = Z_OBJPROP_P(val); zval retval, fname; zend_result return_code; - if (myht && GC_IS_RECURSIVE(myht)) { + if (GC_IS_RECURSIVE(obj)) { encoder->error_code = PHP_JSON_ERROR_RECURSION; if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) { smart_str_appendl(buf, "null", 4); @@ -547,7 +547,7 @@ static zend_result php_json_encode_serializable_object(smart_str *buf, zval *val return FAILURE; } - PHP_JSON_HASH_PROTECT_RECURSION(myht); + GC_PROTECT_RECURSION(obj); ZVAL_STRING(&fname, "jsonSerialize"); @@ -560,7 +560,7 @@ static zend_result php_json_encode_serializable_object(smart_str *buf, zval *val if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) { smart_str_appendl(buf, "null", 4); } - PHP_JSON_HASH_UNPROTECT_RECURSION(myht); + GC_UNPROTECT_RECURSION(obj); return FAILURE; } @@ -572,19 +572,19 @@ static zend_result php_json_encode_serializable_object(smart_str *buf, zval *val if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) { smart_str_appendl(buf, "null", 4); } - PHP_JSON_HASH_UNPROTECT_RECURSION(myht); + GC_UNPROTECT_RECURSION(obj); return FAILURE; } if ((Z_TYPE(retval) == IS_OBJECT) && (Z_OBJ(retval) == Z_OBJ_P(val))) { /* Handle the case where jsonSerialize does: return $this; by going straight to encode array */ - PHP_JSON_HASH_UNPROTECT_RECURSION(myht); + GC_UNPROTECT_RECURSION(obj); return_code = php_json_encode_array(buf, &retval, options, encoder); } else { /* All other types, encode as normal */ return_code = php_json_encode_zval(buf, &retval, options, encoder); - PHP_JSON_HASH_UNPROTECT_RECURSION(myht); + GC_UNPROTECT_RECURSION(obj); } zval_ptr_dtor(&retval); diff --git a/ext/json/tests/bug77843.phpt b/ext/json/tests/bug77843.phpt index 2709612659c61..39180e95d2623 100644 --- a/ext/json/tests/bug77843.phpt +++ b/ext/json/tests/bug77843.phpt @@ -18,8 +18,5 @@ var_dump(json_encode([&$arr])); ?> --EXPECT-- -object(X)#1 (1) { - ["prop"]=> - string(5) "value" -} +*RECURSION* string(20) "[[{"prop":"value"}]]" diff --git a/ext/standard/tests/class_object/get_object_vars_variation_004.phpt b/ext/standard/tests/class_object/get_object_vars_variation_004.phpt index 84827134c1b18..e89b18c02d9a2 100644 --- a/ext/standard/tests/class_object/get_object_vars_variation_004.phpt +++ b/ext/standard/tests/class_object/get_object_vars_variation_004.phpt @@ -43,6 +43,5 @@ array(4) { [12]=> int(6) ["test"]=> - object(JsonSerializable@anonymous)#2 (0) { - } + *RECURSION* }