diff --git a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt index 5c4d1cb87f1d7..5a230936fff96 100644 --- a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt @@ -8,7 +8,7 @@ $standard = new ReflectionExtension('standard'); var_dump($standard->getClassNames()); ?> --EXPECT-- -array(4) { +array(5) { [0]=> string(22) "__PHP_Incomplete_Class" [1]=> @@ -16,5 +16,7 @@ array(4) { [2]=> string(15) "php_user_filter" [3]=> + string(12) "StreamBucket" + [4]=> string(9) "Directory" } diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index c3adaceff4390..dabb9fc1434a5 100644 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -3744,19 +3744,19 @@ function get_headers(string $url, bool $associative = false, $context = null): a * @param resource $brigade * @refcount 1 */ -function stream_bucket_make_writeable($brigade): ?object {} +function stream_bucket_make_writeable($brigade): ?StreamBucket {} /** @param resource $brigade */ -function stream_bucket_prepend($brigade, object $bucket): void {} +function stream_bucket_prepend($brigade, StreamBucket $bucket): void {} /** @param resource $brigade */ -function stream_bucket_append($brigade, object $bucket): void {} +function stream_bucket_append($brigade, StreamBucket $bucket): void {} /** * @param resource $stream * @refcount 1 */ -function stream_bucket_new($stream, string $buffer): object {} +function stream_bucket_new($stream, string $buffer): StreamBucket {} /** * @return array diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index c420e5c29e0f7..4556e10ada82b 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 954bf48ac1f24a14fd7508c8bf4b781883398499 */ + * Stub hash: 1ef54fdebc6a206c4af3438130db0cd12a62c8b6 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) @@ -2104,18 +2104,18 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_get_headers, 0, 1, MAY_BE_ARRAY| ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, context, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stream_bucket_make_writeable, 0, 1, IS_OBJECT, 1) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_stream_bucket_make_writeable, 0, 1, StreamBucket, 1) ZEND_ARG_INFO(0, brigade) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stream_bucket_prepend, 0, 2, IS_VOID, 0) ZEND_ARG_INFO(0, brigade) - ZEND_ARG_TYPE_INFO(0, bucket, IS_OBJECT, 0) + ZEND_ARG_OBJ_INFO(0, bucket, StreamBucket, 0) ZEND_END_ARG_INFO() #define arginfo_stream_bucket_append arginfo_stream_bucket_prepend -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stream_bucket_new, 0, 2, IS_OBJECT, 0) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_stream_bucket_new, 0, 2, StreamBucket, 0) ZEND_ARG_INFO(0, stream) ZEND_ARG_TYPE_INFO(0, buffer, IS_STRING, 0) ZEND_END_ARG_INFO() diff --git a/ext/standard/tests/file/bug39551.phpt b/ext/standard/tests/file/bug39551.phpt index e6982fcaab10a..5b0674342dea5 100644 --- a/ext/standard/tests/file/bug39551.phpt +++ b/ext/standard/tests/file/bug39551.phpt @@ -4,6 +4,7 @@ Bug #39551 (Segfault with stream_bucket_new in user filter) ---EXPECT-- +--EXPECTF-- +object(StreamBucket)#%d (%d) { + ["bucket"]=> + resource(%d) of type (userfilter.bucket) + ["data"]=> + string(0) "" + ["datalen"]=> + int(0) + ["dataLength"]=> + int(0) +} Done diff --git a/ext/standard/user_filters.c b/ext/standard/user_filters.c index 9cdd1b26b7de6..f4ffae9559276 100644 --- a/ext/standard/user_filters.c +++ b/ext/standard/user_filters.c @@ -61,6 +61,7 @@ PHP_METHOD(php_user_filter, onClose) } static zend_class_entry *user_filter_class_entry; +static zend_class_entry *stream_bucket_class_entry; static ZEND_RSRC_DTOR_FUNC(php_bucket_dtor) { @@ -75,6 +76,7 @@ PHP_MINIT_FUNCTION(user_filters) { /* init the filter class ancestor */ user_filter_class_entry = register_class_php_user_filter(); + stream_bucket_class_entry = register_class_StreamBucket(); /* Filters will dispose of their brigades */ le_bucket_brigade = zend_register_list_destructors_ex(NULL, NULL, PHP_STREAM_BRIGADE_RES_NAME, module_number); @@ -351,16 +353,17 @@ PHP_FUNCTION(stream_bucket_make_writeable) RETURN_THROWS(); } - ZVAL_NULL(return_value); - if (brigade->head && (bucket = php_stream_bucket_make_writeable(brigade->head))) { ZVAL_RES(&zbucket, zend_register_resource(bucket, le_bucket)); - object_init(return_value); - add_property_zval(return_value, "bucket", &zbucket); + object_init_ex(return_value, stream_bucket_class_entry); + zend_update_property(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("bucket"), &zbucket); /* add_property_zval increments the refcount which is unwanted here */ zval_ptr_dtor(&zbucket); - add_property_stringl(return_value, "data", bucket->buf, bucket->buflen); - add_property_long(return_value, "datalen", bucket->buflen); + zend_update_property_stringl(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("data"), bucket->buf, bucket->buflen); + zend_update_property_long(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("datalen"), bucket->buflen); + zend_update_property_long(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("dataLength"), bucket->buflen); + } else { + ZVAL_NULL(return_value); } } /* }}} */ @@ -369,30 +372,32 @@ PHP_FUNCTION(stream_bucket_make_writeable) static void php_stream_bucket_attach(int append, INTERNAL_FUNCTION_PARAMETERS) { zval *zbrigade, *zobject; - zval *pzbucket, *pzdata; + zval *pzbucket, *pzdata, rv; php_stream_bucket_brigade *brigade; php_stream_bucket *bucket; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_RESOURCE(zbrigade) - Z_PARAM_OBJECT(zobject) + Z_PARAM_OBJECT_OF_CLASS(zobject, stream_bucket_class_entry) ZEND_PARSE_PARAMETERS_END(); - if (NULL == (pzbucket = zend_hash_str_find_deref(Z_OBJPROP_P(zobject), "bucket", sizeof("bucket")-1))) { - zend_argument_value_error(2, "must be an object that has a \"bucket\" property"); + if ((brigade = (php_stream_bucket_brigade*)zend_fetch_resource( + Z_RES_P(zbrigade), PHP_STREAM_BRIGADE_RES_NAME, le_bucket_brigade)) == NULL) { RETURN_THROWS(); } - if ((brigade = (php_stream_bucket_brigade*)zend_fetch_resource( - Z_RES_P(zbrigade), PHP_STREAM_BRIGADE_RES_NAME, le_bucket_brigade)) == NULL) { + if (NULL == (pzbucket = zend_read_property(NULL, Z_OBJ_P(zobject), "bucket", sizeof("bucket")-1, false, &rv))) { + zend_argument_value_error(2, "must be an object that has a \"bucket\" property"); RETURN_THROWS(); } + ZVAL_DEREF(pzbucket); if ((bucket = (php_stream_bucket *)zend_fetch_resource_ex(pzbucket, PHP_STREAM_BUCKET_RES_NAME, le_bucket)) == NULL) { RETURN_THROWS(); } - if (NULL != (pzdata = zend_hash_str_find_deref(Z_OBJPROP_P(zobject), "data", sizeof("data")-1)) && Z_TYPE_P(pzdata) == IS_STRING) { + if (NULL != (pzdata = zend_read_property(NULL, Z_OBJ_P(zobject), "data", sizeof("data")-1, false, &rv))) { + ZVAL_DEREF(pzdata); if (!bucket->own_buf) { bucket = php_stream_bucket_make_writeable(bucket); } @@ -454,12 +459,13 @@ PHP_FUNCTION(stream_bucket_new) bucket = php_stream_bucket_new(stream, pbuffer, buffer_len, 1, php_stream_is_persistent(stream)); ZVAL_RES(&zbucket, zend_register_resource(bucket, le_bucket)); - object_init(return_value); - add_property_zval(return_value, "bucket", &zbucket); + object_init_ex(return_value, stream_bucket_class_entry); + zend_update_property(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("bucket"), &zbucket); /* add_property_zval increments the refcount which is unwanted here */ zval_ptr_dtor(&zbucket); - add_property_stringl(return_value, "data", bucket->buf, bucket->buflen); - add_property_long(return_value, "datalen", bucket->buflen); + zend_update_property_stringl(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("data"), bucket->buf, bucket->buflen); + zend_update_property_long(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("datalen"), bucket->buflen); + zend_update_property_long(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("dataLength"), bucket->buflen); } /* }}} */ diff --git a/ext/standard/user_filters.stub.php b/ext/standard/user_filters.stub.php index 3c134ea1bb5b9..acaa42bb33c84 100644 --- a/ext/standard/user_filters.stub.php +++ b/ext/standard/user_filters.stub.php @@ -55,3 +55,18 @@ public function onCreate(): bool {} /** @tentative-return-type */ public function onClose(): void {} } + +final class StreamBucket +{ + /** + * @var resource + * @readonly + */ + public $bucket; + /** @readonly */ + public string $data; + /** @readonly */ + public int $datalen; + /** @readonly */ + public int $dataLength; +} diff --git a/ext/standard/user_filters_arginfo.h b/ext/standard/user_filters_arginfo.h index 53c535b4eb9af..a40a00f7ee476 100644 --- a/ext/standard/user_filters_arginfo.h +++ b/ext/standard/user_filters_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 1c03251b4e0b22056da43bf86087d6996454d2a0 */ + * Stub hash: 33264435fe01a2cc9aa21a4a087dbbf3c4007206 */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_php_user_filter_filter, 0, 4, IS_LONG, 0) ZEND_ARG_INFO(0, in) @@ -25,6 +25,10 @@ static const zend_function_entry class_php_user_filter_methods[] = { ZEND_FE_END }; +static const zend_function_entry class_StreamBucket_methods[] = { + ZEND_FE_END +}; + static void register_user_filters_symbols(int module_number) { REGISTER_LONG_CONSTANT("PSFS_PASS_ON", PSFS_PASS_ON, CONST_PERSISTENT); @@ -62,3 +66,38 @@ static zend_class_entry *register_class_php_user_filter(void) return class_entry; } + +static zend_class_entry *register_class_StreamBucket(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "StreamBucket", class_StreamBucket_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_FINAL; + + zval property_bucket_default_value; + ZVAL_NULL(&property_bucket_default_value); + zend_string *property_bucket_name = zend_string_init("bucket", sizeof("bucket") - 1, 1); + zend_declare_typed_property(class_entry, property_bucket_name, &property_bucket_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_NONE(0)); + zend_string_release(property_bucket_name); + + zval property_data_default_value; + ZVAL_UNDEF(&property_data_default_value); + zend_string *property_data_name = zend_string_init("data", sizeof("data") - 1, 1); + zend_declare_typed_property(class_entry, property_data_name, &property_data_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_data_name); + + zval property_datalen_default_value; + ZVAL_UNDEF(&property_datalen_default_value); + zend_string *property_datalen_name = zend_string_init("datalen", sizeof("datalen") - 1, 1); + zend_declare_typed_property(class_entry, property_datalen_name, &property_datalen_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_datalen_name); + + zval property_dataLength_default_value; + ZVAL_UNDEF(&property_dataLength_default_value); + zend_string *property_dataLength_name = zend_string_init("dataLength", sizeof("dataLength") - 1, 1); + zend_declare_typed_property(class_entry, property_dataLength_name, &property_dataLength_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_dataLength_name); + + return class_entry; +}