mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
ext/standard/stream: Use FCC instead of zval for notification callback (#19024)
Also check that the callable exists while setting the option
This commit is contained in:
parent
c33805791d
commit
677a1f80c8
5 changed files with 76 additions and 26 deletions
|
@ -856,10 +856,7 @@ PHP_FUNCTION(stream_select)
|
|||
static void user_space_stream_notifier(php_stream_context *context, int notifycode, int severity,
|
||||
char *xmsg, int xcode, size_t bytes_sofar, size_t bytes_max, void * ptr)
|
||||
{
|
||||
zval *callback = &context->notifier->ptr;
|
||||
zval retval;
|
||||
zval zvs[6];
|
||||
int i;
|
||||
|
||||
ZVAL_LONG(&zvs[0], notifycode);
|
||||
ZVAL_LONG(&zvs[1], severity);
|
||||
|
@ -872,21 +869,19 @@ static void user_space_stream_notifier(php_stream_context *context, int notifyco
|
|||
ZVAL_LONG(&zvs[4], bytes_sofar);
|
||||
ZVAL_LONG(&zvs[5], bytes_max);
|
||||
|
||||
if (FAILURE == call_user_function(NULL, NULL, callback, &retval, 6, zvs)) {
|
||||
php_error_docref(NULL, E_WARNING, "Failed to call user notifier");
|
||||
}
|
||||
for (i = 0; i < 6; i++) {
|
||||
zval_ptr_dtor(&zvs[i]);
|
||||
}
|
||||
zval_ptr_dtor(&retval);
|
||||
zend_call_known_fcc(context->notifier->fcc, NULL, 6, zvs, NULL);
|
||||
/* Free refcounted string parameter */
|
||||
zval_ptr_dtor_str(&zvs[2]);
|
||||
}
|
||||
|
||||
static void user_space_stream_notifier_dtor(php_stream_notifier *notifier)
|
||||
{
|
||||
if (notifier && Z_TYPE(notifier->ptr) != IS_UNDEF) {
|
||||
zval_ptr_dtor(¬ifier->ptr);
|
||||
ZVAL_UNDEF(¬ifier->ptr);
|
||||
}
|
||||
ZEND_ASSERT(notifier);
|
||||
ZEND_ASSERT(notifier->fcc);
|
||||
ZEND_ASSERT(notifier->fcc->function_handler);
|
||||
zend_fcc_dtor(notifier->fcc);
|
||||
efree(notifier->fcc);
|
||||
notifier->fcc = NULL;
|
||||
}
|
||||
|
||||
static zend_result parse_context_options(php_stream_context *context, HashTable *options)
|
||||
|
@ -924,9 +919,19 @@ static zend_result parse_context_params(php_stream_context *context, HashTable *
|
|||
context->notifier = NULL;
|
||||
}
|
||||
|
||||
zend_fcall_info_cache *fcc = emalloc(sizeof(*fcc));
|
||||
char *error;
|
||||
if (!zend_is_callable_ex(tmp, NULL, 0, NULL, fcc, &error)) {
|
||||
zend_argument_type_error(1, "must be an array with valid callbacks as values, %s", error);
|
||||
efree(fcc);
|
||||
efree(error);
|
||||
return FAILURE;
|
||||
}
|
||||
zend_fcc_addref(fcc);
|
||||
|
||||
context->notifier = php_stream_notification_alloc();
|
||||
context->notifier->func = user_space_stream_notifier;
|
||||
ZVAL_COPY(&context->notifier->ptr, tmp);
|
||||
context->notifier->fcc = fcc;
|
||||
context->notifier->dtor = user_space_stream_notifier_dtor;
|
||||
}
|
||||
if (NULL != (tmp = zend_hash_str_find(params, "options", sizeof("options")-1))) {
|
||||
|
@ -1123,9 +1128,11 @@ PHP_FUNCTION(stream_context_get_params)
|
|||
}
|
||||
|
||||
array_init(return_value);
|
||||
if (context->notifier && Z_TYPE(context->notifier->ptr) != IS_UNDEF && context->notifier->func == user_space_stream_notifier) {
|
||||
Z_TRY_ADDREF(context->notifier->ptr);
|
||||
add_assoc_zval_ex(return_value, "notification", sizeof("notification")-1, &context->notifier->ptr);
|
||||
if (context->notifier && context->notifier->fcc) {
|
||||
ZEND_ASSERT(context->notifier->func == user_space_stream_notifier);
|
||||
zval fn;
|
||||
zend_get_callable_zval_from_fcc(context->notifier->fcc, &fn);
|
||||
add_assoc_zval_ex(return_value, ZEND_STRL("notification"), &fn);
|
||||
}
|
||||
Z_TRY_ADDREF(context->options);
|
||||
add_assoc_zval_ex(return_value, "options", sizeof("options")-1, &context->options);
|
||||
|
|
|
@ -4,16 +4,19 @@ stream_context_get_params()
|
|||
<?php
|
||||
|
||||
$ctx = stream_context_create();
|
||||
var_dump($ctx);
|
||||
var_dump(stream_context_get_params($ctx));
|
||||
|
||||
var_dump(stream_context_set_option($ctx, "foo","bar","baz"));
|
||||
var_dump(stream_context_get_params($ctx));
|
||||
|
||||
function stream_notification_callback() {}
|
||||
var_dump(stream_context_set_params($ctx, array("notification" => "stream_notification_callback")));
|
||||
var_dump(stream_context_get_params($ctx));
|
||||
|
||||
var_dump(stream_context_set_params($ctx, array("notification" => array("stream","notification_callback"))));
|
||||
class MyStream {
|
||||
public static function notification_callback() {}
|
||||
}
|
||||
var_dump(stream_context_set_params($ctx, array("notification" => ["MyStream", "notification_callback"])));
|
||||
var_dump(stream_context_get_params($ctx));
|
||||
|
||||
var_dump(stream_context_get_params($ctx));
|
||||
|
@ -22,8 +25,7 @@ var_dump(stream_context_get_params($ctx));
|
|||
var_dump(stream_context_get_options($ctx));
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
resource(%d) of type (stream-context)
|
||||
--EXPECT--
|
||||
array(1) {
|
||||
["options"]=>
|
||||
array(0) {
|
||||
|
@ -58,7 +60,7 @@ array(2) {
|
|||
["notification"]=>
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(6) "stream"
|
||||
string(8) "MyStream"
|
||||
[1]=>
|
||||
string(21) "notification_callback"
|
||||
}
|
||||
|
@ -75,7 +77,7 @@ array(2) {
|
|||
["notification"]=>
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(6) "stream"
|
||||
string(8) "MyStream"
|
||||
[1]=>
|
||||
string(21) "notification_callback"
|
||||
}
|
||||
|
@ -99,7 +101,7 @@ array(2) {
|
|||
["notification"]=>
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(6) "stream"
|
||||
string(8) "MyStream"
|
||||
[1]=>
|
||||
string(21) "notification_callback"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
--TEST--
|
||||
stream_context_set_params() with invalid notification option
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$ctx = stream_context_create();
|
||||
try {
|
||||
var_dump(stream_context_set_params($ctx, ["notification" => "fn_not_exist"]));
|
||||
} catch (\Throwable $e) {
|
||||
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
try {
|
||||
var_dump(stream_context_set_params($ctx, ["notification" => ["myclass", "fn_not_exist"]]));
|
||||
} catch (\Throwable $e) {
|
||||
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
TypeError: stream_context_set_params(): Argument #1 ($context) must be an array with valid callbacks as values, function "fn_not_exist" not found or invalid function name
|
||||
TypeError: stream_context_set_params(): Argument #1 ($context) must be an array with valid callbacks as values, class "myclass" not found
|
|
@ -0,0 +1,20 @@
|
|||
--TEST--
|
||||
stream_context_set_params() with valid, then invalid notification option
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function foo() {}
|
||||
|
||||
$ctx = stream_context_create();
|
||||
var_dump(stream_context_set_params($ctx, ["notification" => "foo"]));
|
||||
|
||||
try {
|
||||
var_dump(stream_context_set_params($ctx, ["notification" => "fn_not_exist"]));
|
||||
} catch (\Throwable $e) {
|
||||
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
bool(true)
|
||||
TypeError: stream_context_set_params(): Argument #1 ($context) must be an array with valid callbacks as values, function "fn_not_exist" not found or invalid function name
|
|
@ -44,7 +44,7 @@ typedef struct _php_stream_notifier php_stream_notifier;
|
|||
struct _php_stream_notifier {
|
||||
php_stream_notification_func func;
|
||||
void (*dtor)(php_stream_notifier *notifier);
|
||||
zval ptr;
|
||||
zend_fcall_info_cache *fcc;
|
||||
int mask;
|
||||
size_t progress, progress_max; /* position for progress notification */
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue