Skip to content

Commit ac7dd35

Browse files
committed
ext/curl: Convert handlers.progress to just be a FCC
1 parent 71194ea commit ac7dd35

File tree

5 files changed

+121
-74
lines changed

5 files changed

+121
-74
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
@@ -488,8 +488,8 @@ static HashTable *curl_get_gc(zend_object *object, zval **table, int *n)
488488
zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write_header->stream);
489489
}
490490

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

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

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

676-
zval argv[5];
675+
zval args[5];
677676
zval retval;
678-
zend_result error;
679-
zend_fcall_info fci;
680677

681678
GC_ADDREF(&ch->std);
682-
ZVAL_OBJ(&argv[0], &ch->std);
683-
ZVAL_LONG(&argv[1], (zend_long)dltotal);
684-
ZVAL_LONG(&argv[2], (zend_long)dlnow);
685-
ZVAL_LONG(&argv[3], (zend_long)ultotal);
686-
ZVAL_LONG(&argv[4], (zend_long)ulnow);
687-
688-
fci.size = sizeof(fci);
689-
ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);
690-
fci.object = NULL;
691-
fci.retval = &retval;
692-
fci.param_count = 5;
693-
fci.params = argv;
694-
fci.named_params = NULL;
695-
696-
ch->in_callback = 1;
697-
error = zend_call_function(&fci, &t->fci_cache);
698-
ch->in_callback = 0;
699-
if (error == FAILURE) {
700-
php_error_docref(NULL, E_WARNING, "Cannot call the CURLOPT_PROGRESSFUNCTION");
701-
} else if (!Z_ISUNDEF(retval)) {
702-
_php_curl_verify_handlers(ch, /* reporterror */ true);
703-
if (0 != zval_get_long(&retval)) {
704-
rval = 1;
705-
}
706-
}
707-
zval_ptr_dtor(&argv[0]);
679+
ZVAL_OBJ(&args[0], &ch->std);
680+
ZVAL_LONG(&args[1], (zend_long)dltotal);
681+
ZVAL_LONG(&args[2], (zend_long)dlnow);
682+
ZVAL_LONG(&args[3], (zend_long)ultotal);
683+
ZVAL_LONG(&args[4], (zend_long)ulnow);
684+
685+
ch->in_callback = true;
686+
zend_call_known_fcc(&ch->handlers.progress, &retval, /* param_count */ 5, args, /* named_params */ NULL);
687+
ch->in_callback = false;
688+
689+
if (!Z_ISUNDEF(retval)) {
690+
_php_curl_verify_handlers(ch, /* reporterror */ true);
691+
/* TODO Check callback returns an int or something castable to int */
692+
if (0 != zval_get_long(&retval)) {
693+
rval = 1;
694+
}
695+
}
696+
697+
zval_ptr_dtor(&args[0]);
708698
return rval;
709699
}
710700
/* }}} */
@@ -1117,7 +1107,7 @@ void init_curl_handle(php_curl *ch)
11171107
ch->handlers.write = ecalloc(1, sizeof(php_curl_write));
11181108
ch->handlers.write_header = ecalloc(1, sizeof(php_curl_write));
11191109
ch->handlers.read = ecalloc(1, sizeof(php_curl_read));
1120-
ch->handlers.progress = NULL;
1110+
ch->handlers.progress = empty_fcall_info_cache;
11211111
ch->handlers.xferinfo = NULL;
11221112
ch->handlers.fnmatch = NULL;
11231113
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
@@ -1254,6 +1244,14 @@ static void _php_copy_callback(php_curl *ch, php_curl_callback **new_callback, p
12541244
}
12551245
}
12561246

1247+
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)
1248+
{
1249+
if (ZEND_FCC_INITIALIZED(*source_fcc)) {
1250+
zend_fcc_dup(target_fcc, source_fcc);
1251+
curl_easy_setopt(ch->cp, option, (void *) ch);
1252+
}
1253+
}
1254+
12571255
void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source)
12581256
{
12591257
if (!Z_ISUNDEF(source->handlers.write->stream)) {
@@ -1293,7 +1291,7 @@ void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source)
12931291
curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch);
12941292
curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *) ch);
12951293

1296-
_php_copy_callback(ch, &ch->handlers.progress, source->handlers.progress, CURLOPT_PROGRESSDATA);
1294+
php_curl_copy_fcc_with_option(ch, CURLOPT_PROGRESSDATA, &ch->handlers.progress, &source->handlers.progress);
12971295
_php_copy_callback(ch, &ch->handlers.xferinfo, source->handlers.xferinfo, CURLOPT_XFERINFODATA);
12981296
_php_copy_callback(ch, &ch->handlers.fnmatch, source->handlers.fnmatch, CURLOPT_FNMATCH_DATA);
12991297
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
@@ -1598,6 +1596,25 @@ PHP_FUNCTION(curl_copy_handle)
15981596
}
15991597
/* }}} */
16001598

1599+
static bool php_curl_set_callable_handler(zend_fcall_info_cache *const handler_fcc, zval *callable, bool is_array_config, const char *option_name)
1600+
{
1601+
if (ZEND_FCC_INITIALIZED(*handler_fcc)) {
1602+
zend_fcc_dtor(handler_fcc);
1603+
handler_fcc->function_handler = NULL;
1604+
}
1605+
1606+
char *error = NULL;
1607+
if (UNEXPECTED(!zend_is_callable_ex(callable, /* object */ NULL, /* check_flags */ 0, /* callable_name */ NULL, handler_fcc, /* error */ &error))) {
1608+
if (!EG(exception)) {
1609+
zend_argument_type_error(2 + !is_array_config, "must be a valid callback for option %s, %s", option_name, error);
1610+
}
1611+
efree(error);
1612+
return false;
1613+
}
1614+
zend_fcc_addref(handler_fcc);
1615+
return true;
1616+
}
1617+
16011618
static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool is_array_config) /* {{{ */
16021619
{
16031620
CURLcode error = CURLE_OK;
@@ -2142,17 +2159,17 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
21422159
}
21432160
break;
21442161

2145-
case CURLOPT_PROGRESSFUNCTION:
2162+
case CURLOPT_PROGRESSFUNCTION: {
2163+
/* Check value is actually a callable and set it */
2164+
const char option_name[] = "CURLOPT_PROGRESSFUNCTION";
2165+
bool result = php_curl_set_callable_handler(&ch->handlers.progress, zvalue, is_array_config, option_name);
2166+
if (!result) {
2167+
return FAILURE;
2168+
}
21462169
curl_easy_setopt(ch->cp, CURLOPT_PROGRESSFUNCTION, curl_progress);
21472170
curl_easy_setopt(ch->cp, CURLOPT_PROGRESSDATA, ch);
2148-
if (ch->handlers.progress == NULL) {
2149-
ch->handlers.progress = ecalloc(1, sizeof(php_curl_callback));
2150-
} else if (!Z_ISUNDEF(ch->handlers.progress->func_name)) {
2151-
zval_ptr_dtor(&ch->handlers.progress->func_name);
2152-
ch->handlers.progress->fci_cache = empty_fcall_info_cache;
2153-
}
2154-
ZVAL_COPY(&ch->handlers.progress->func_name, zvalue);
21552171
break;
2172+
}
21562173

21572174
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
21582175
case CURLOPT_SSH_HOSTKEYFUNCTION:
@@ -2843,7 +2860,9 @@ static void curl_free_obj(zend_object *object)
28432860
efree(ch->handlers.write_header);
28442861
efree(ch->handlers.read);
28452862

2846-
_php_curl_free_callback(ch->handlers.progress);
2863+
if (ZEND_FCC_INITIALIZED(ch->handlers.progress)) {
2864+
zend_fcc_dtor(&ch->handlers.progress);
2865+
}
28472866
_php_curl_free_callback(ch->handlers.xferinfo);
28482867
_php_curl_free_callback(ch->handlers.fnmatch);
28492868
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
@@ -2911,10 +2930,8 @@ static void _php_curl_reset_handlers(php_curl *ch)
29112930
ZVAL_UNDEF(&ch->handlers.std_err);
29122931
}
29132932

2914-
if (ch->handlers.progress) {
2915-
zval_ptr_dtor(&ch->handlers.progress->func_name);
2916-
efree(ch->handlers.progress);
2917-
ch->handlers.progress = NULL;
2933+
if (ZEND_FCC_INITIALIZED(ch->handlers.progress)) {
2934+
zend_fcc_dtor(&ch->handlers.progress);
29182935
}
29192936

29202937
if (ch->handlers.xferinfo) {

ext/curl/tests/curl_copy_handle_basic_008.phpt

Lines changed: 0 additions & 26 deletions
This file was deleted.

ext/curl/tests/curl_progress.phpt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
Test curl_copy_handle() with CURLOPT_PROGRESSFUNCTION
3+
--EXTENSIONS--
4+
curl
5+
--FILE--
6+
<?php
7+
include 'server.inc';
8+
$host = curl_cli_server_start();
9+
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);
19+
20+
?>
21+
--EXPECT--
22+
Hello World!
23+
Hello World!
24+
Hello World!
25+
Hello World!
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)