Add dedicated StreamBucket class (#13111)

RFC: https://wiki.php.net/rfc/dedicated_stream_bucket
This commit is contained in:
Máté Kocsis 2024-04-11 20:11:40 +02:00 committed by GitHub
parent bc62b41126
commit be2f454d6e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 103 additions and 30 deletions

View file

@ -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"
}

View file

@ -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<int, string>

View file

@ -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()

View file

@ -4,6 +4,7 @@ Bug #39551 (Segfault with stream_bucket_new in user filter)
<?php
$bucket = stream_bucket_new(fopen('php://temp', 'w+'), '');
var_dump($bucket);
class bucketFilter extends php_user_filter {
public function filter($in, $out, &$consumed, $closing ): int {
@ -20,5 +21,15 @@ stream_get_contents($s);
echo "Done\n";
?>
--EXPECT--
--EXPECTF--
object(StreamBucket)#%d (%d) {
["bucket"]=>
resource(%d) of type (userfilter.bucket)
["data"]=>
string(0) ""
["datalen"]=>
int(0)
["dataLength"]=>
int(0)
}
Done

View file

@ -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))) {
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 (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();
}
if ((brigade = (php_stream_bucket_brigade*)zend_fetch_resource(
Z_RES_P(zbrigade), PHP_STREAM_BRIGADE_RES_NAME, le_bucket_brigade)) == NULL) {
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);
}
/* }}} */

View file

@ -55,3 +55,18 @@ class php_user_filter
/** @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;
}

View file

@ -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;
}