Skip to content

Commit 9235663

Browse files
committed
ext/curl: Convert handlers.progress to just be a FCC
1 parent edb9f65 commit 9235663

File tree

4 files changed

+107
-60
lines changed

4 files changed

+107
-60
lines changed

ext/curl/curl_private.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ typedef struct {
7272
php_curl_write *write_header;
7373
php_curl_read *read;
7474
zval std_err;
75-
php_curl_callback *progress;
75+
zend_fcall_info_cache progress;
7676
php_curl_callback *xferinfo;
7777
php_curl_callback *fnmatch;
7878
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */

ext/curl/interface.c

Lines changed: 64 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -487,8 +487,8 @@ static HashTable *curl_get_gc(zend_object *object, zval **table, int *n)
487487
zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write_header->stream);
488488
}
489489

490-
if (curl->handlers.progress) {
491-
zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.progress->func_name);
490+
if (ZEND_FCC_INITIALIZED(curl->handlers.progress)) {
491+
zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.progress);
492492
}
493493

494494
if (curl->handlers.xferinfo) {
@@ -664,46 +664,36 @@ static int curl_fnmatch(void *ctx, const char *pattern, const char *string)
664664
static size_t curl_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
665665
{
666666
php_curl *ch = (php_curl *)clientp;
667-
php_curl_callback *t = ch->handlers.progress;
668667
size_t rval = 0;
669668

670669
#if PHP_CURL_DEBUG
671670
fprintf(stderr, "curl_progress() called\n");
672671
fprintf(stderr, "clientp = %x, dltotal = %f, dlnow = %f, ultotal = %f, ulnow = %f\n", clientp, dltotal, dlnow, ultotal, ulnow);
673672
#endif
674673

675-
zval argv[5];
674+
zval args[5];
676675
zval retval;
677-
zend_result error;
678-
zend_fcall_info fci;
679676

680677
GC_ADDREF(&ch->std);
681-
ZVAL_OBJ(&argv[0], &ch->std);
682-
ZVAL_LONG(&argv[1], (zend_long)dltotal);
683-
ZVAL_LONG(&argv[2], (zend_long)dlnow);
684-
ZVAL_LONG(&argv[3], (zend_long)ultotal);
685-
ZVAL_LONG(&argv[4], (zend_long)ulnow);
686-
687-
fci.size = sizeof(fci);
688-
ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);
689-
fci.object = NULL;
690-
fci.retval = &retval;
691-
fci.param_count = 5;
692-
fci.params = argv;
693-
fci.named_params = NULL;
694-
695-
ch->in_callback = 1;
696-
error = zend_call_function(&fci, &t->fci_cache);
697-
ch->in_callback = 0;
698-
if (error == FAILURE) {
699-
php_error_docref(NULL, E_WARNING, "Cannot call the CURLOPT_PROGRESSFUNCTION");
700-
} else if (!Z_ISUNDEF(retval)) {
701-
_php_curl_verify_handlers(ch, /* reporterror */ true);
702-
if (0 != zval_get_long(&retval)) {
703-
rval = 1;
704-
}
705-
}
706-
zval_ptr_dtor(&argv[0]);
678+
ZVAL_OBJ(&args[0], &ch->std);
679+
ZVAL_LONG(&args[1], (zend_long)dltotal);
680+
ZVAL_LONG(&args[2], (zend_long)dlnow);
681+
ZVAL_LONG(&args[3], (zend_long)ultotal);
682+
ZVAL_LONG(&args[4], (zend_long)ulnow);
683+
684+
ch->in_callback = true;
685+
zend_call_known_fcc(&ch->handlers.progress, &retval, /* param_count */ 5, args, /* named_params */ NULL);
686+
ch->in_callback = false;
687+
688+
if (!Z_ISUNDEF(retval)) {
689+
_php_curl_verify_handlers(ch, /* reporterror */ true);
690+
/* TODO Check callback returns an int or something castable to in */
691+
if (0 != zval_get_long(&retval)) {
692+
rval = 1;
693+
}
694+
}
695+
696+
zval_ptr_dtor(&args[0]);
707697
return rval;
708698
}
709699
/* }}} */
@@ -1054,7 +1044,7 @@ void init_curl_handle(php_curl *ch)
10541044
ch->handlers.write = ecalloc(1, sizeof(php_curl_write));
10551045
ch->handlers.write_header = ecalloc(1, sizeof(php_curl_write));
10561046
ch->handlers.read = ecalloc(1, sizeof(php_curl_read));
1057-
ch->handlers.progress = NULL;
1047+
ch->handlers.progress = empty_fcall_info_cache;
10581048
ch->handlers.xferinfo = NULL;
10591049
ch->handlers.fnmatch = NULL;
10601050
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
@@ -1191,6 +1181,14 @@ static void _php_copy_callback(php_curl *ch, php_curl_callback **new_callback, p
11911181
}
11921182
}
11931183

1184+
static void php_curl_copy_fcc_with_option(php_curl *ch, CURLoption option, zend_fcall_info_cache *target_fcc, zend_fcall_info_cache *source_fcc)
1185+
{
1186+
if (ZEND_FCC_INITIALIZED(*source_fcc)) {
1187+
zend_fcc_dup(target_fcc, source_fcc);
1188+
curl_easy_setopt(ch->cp, option, (void *) ch);
1189+
}
1190+
}
1191+
11941192
void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source)
11951193
{
11961194
if (!Z_ISUNDEF(source->handlers.write->stream)) {
@@ -1230,7 +1228,7 @@ void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source)
12301228
curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch);
12311229
curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *) ch);
12321230

1233-
_php_copy_callback(ch, &ch->handlers.progress, source->handlers.progress, CURLOPT_PROGRESSDATA);
1231+
php_curl_copy_fcc_with_option(ch, CURLOPT_PROGRESSDATA, &ch->handlers.progress, &source->handlers.progress);
12341232
_php_copy_callback(ch, &ch->handlers.xferinfo, source->handlers.xferinfo, CURLOPT_XFERINFODATA);
12351233
_php_copy_callback(ch, &ch->handlers.fnmatch, source->handlers.fnmatch, CURLOPT_FNMATCH_DATA);
12361234
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
@@ -1535,6 +1533,25 @@ PHP_FUNCTION(curl_copy_handle)
15351533
}
15361534
/* }}} */
15371535

1536+
static bool php_curl_set_callable_handler(zend_fcall_info_cache *const handler_fcc, zval *callable, bool is_array_config, const char *option_name)
1537+
{
1538+
if (ZEND_FCC_INITIALIZED(*handler_fcc)) {
1539+
zend_fcc_dtor(handler_fcc);
1540+
handler_fcc->function_handler = NULL;
1541+
}
1542+
1543+
char *error = NULL;
1544+
if (UNEXPECTED(!zend_is_callable_ex(callable, /* object */ NULL, /* check_flags */ 0, /* callable_name */ NULL, handler_fcc, /* error */ &error))) {
1545+
if (!EG(exception)) {
1546+
zend_argument_type_error(2 + !is_array_config, "must be a valid callback for option %s, %s", option_name, error);
1547+
}
1548+
efree(error);
1549+
return false;
1550+
}
1551+
zend_fcc_addref(handler_fcc);
1552+
return true;
1553+
}
1554+
15381555
static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool is_array_config) /* {{{ */
15391556
{
15401557
CURLcode error = CURLE_OK;
@@ -2079,17 +2096,17 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
20792096
}
20802097
break;
20812098

2082-
case CURLOPT_PROGRESSFUNCTION:
2099+
case CURLOPT_PROGRESSFUNCTION: {
2100+
/* Check value is actually a callable and set it */
2101+
const char option_name[] = "CURLOPT_PROGRESSFUNCTION";
2102+
bool result = php_curl_set_callable_handler(&ch->handlers.progress, zvalue, is_array_config, option_name);
2103+
if (!result) {
2104+
return FAILURE;
2105+
}
20832106
curl_easy_setopt(ch->cp, CURLOPT_PROGRESSFUNCTION, curl_progress);
20842107
curl_easy_setopt(ch->cp, CURLOPT_PROGRESSDATA, ch);
2085-
if (ch->handlers.progress == NULL) {
2086-
ch->handlers.progress = ecalloc(1, sizeof(php_curl_callback));
2087-
} else if (!Z_ISUNDEF(ch->handlers.progress->func_name)) {
2088-
zval_ptr_dtor(&ch->handlers.progress->func_name);
2089-
ch->handlers.progress->fci_cache = empty_fcall_info_cache;
2090-
}
2091-
ZVAL_COPY(&ch->handlers.progress->func_name, zvalue);
20922108
break;
2109+
}
20932110

20942111
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
20952112
case CURLOPT_SSH_HOSTKEYFUNCTION:
@@ -2780,7 +2797,9 @@ static void curl_free_obj(zend_object *object)
27802797
efree(ch->handlers.write_header);
27812798
efree(ch->handlers.read);
27822799

2783-
_php_curl_free_callback(ch->handlers.progress);
2800+
if (ZEND_FCC_INITIALIZED(ch->handlers.progress)) {
2801+
zend_fcc_dtor(&ch->handlers.progress);
2802+
}
27842803
_php_curl_free_callback(ch->handlers.xferinfo);
27852804
_php_curl_free_callback(ch->handlers.fnmatch);
27862805
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
@@ -2848,10 +2867,8 @@ static void _php_curl_reset_handlers(php_curl *ch)
28482867
ZVAL_UNDEF(&ch->handlers.std_err);
28492868
}
28502869

2851-
if (ch->handlers.progress) {
2852-
zval_ptr_dtor(&ch->handlers.progress->func_name);
2853-
efree(ch->handlers.progress);
2854-
ch->handlers.progress = NULL;
2870+
if (ZEND_FCC_INITIALIZED(ch->handlers.progress)) {
2871+
zend_fcc_dtor(&ch->handlers.progress);
28552872
}
28562873

28572874
if (ch->handlers.xferinfo) {

ext/curl/tests/curl_copy_handle_basic_008.phpt

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,18 @@ Test curl_copy_handle() with CURLOPT_PROGRESSFUNCTION
44
curl
55
--FILE--
66
<?php
7-
include 'server.inc';
8-
$host = curl_cli_server_start();
7+
include 'server.inc';
8+
$host = curl_cli_server_start();
99

10-
$url = "{$host}/get.inc";
11-
$ch = curl_init($url);
12-
13-
curl_setopt($ch, CURLOPT_NOPROGRESS, 0);
14-
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
15-
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function() { });
16-
$ch2 = curl_copy_handle($ch);
17-
echo curl_exec($ch), PHP_EOL;
18-
unset($ch);
19-
echo curl_exec($ch2);
10+
$url = "{$host}/get.inc";
11+
$ch = curl_init($url);
12+
curl_setopt($ch, CURLOPT_NOPROGRESS, 0);
13+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
14+
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function() { });
15+
$ch2 = curl_copy_handle($ch);
16+
echo curl_exec($ch), PHP_EOL;
17+
unset($ch);
18+
echo curl_exec($ch2);
2019

2120
?>
2221
--EXPECT--
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
Test curl_setopt(_array)() with options that take callabes
3+
--EXTENSIONS--
4+
curl
5+
--FILE--
6+
<?php
7+
include 'server.inc';
8+
$host = curl_cli_server_start();
9+
10+
function testOption(CurlHandle $handle, int $option) {
11+
try {
12+
var_dump(curl_setopt($handle, $option, 'undefined'));
13+
} catch (Throwable $e) {
14+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
15+
}
16+
17+
try {
18+
var_dump(curl_setopt_array($handle, [$option => 'undefined']));
19+
} catch (Throwable $e) {
20+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
21+
}
22+
}
23+
24+
$url = "{$host}/get.inc";
25+
$ch = curl_init($url);
26+
testOption($ch, CURLOPT_PROGRESSFUNCTION);
27+
28+
?>
29+
--EXPECT--
30+
TypeError: curl_setopt(): Argument #3 ($value) must be a valid callback for option CURLOPT_PROGRESSFUNCTION, function "undefined" not found or invalid function name
31+
TypeError: curl_setopt_array(): Argument #2 ($options) must be a valid callback for option CURLOPT_PROGRESSFUNCTION, function "undefined" not found or invalid function name

0 commit comments

Comments
 (0)