1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

ext/curl: Refactor cURL to only use FCC (#13291)

* ext/curl: Convert handlers.progress to just be a FCC

* ext/curl: Convert handlers.sshhostkey to just be a FCC

* ext/curl: Convert handlers.xferinfo to just be a FCC

* ext/curl: Convert handlers.fnmatch to just be a FCC

* ext/curl: Convert handlers.server_push to just be a FCC

* ext/curl: Convert php_curl_write to just use FCC without a function name zval

* ext/curl: Convert php_curl_read to just use FCC without a function name zval

* ext/curl: Remove workaround for old libcurl

* ext/curl: Create macros to codegen the handling of callable options
This commit is contained in:
Gina Peter Banyard
2024-05-01 15:09:11 +01:00
committed by GitHub
parent 0540a4211d
commit f4dbe2390d
18 changed files with 632 additions and 404 deletions

View File

@@ -45,8 +45,7 @@ PHP_MSHUTDOWN_FUNCTION(curl);
PHP_MINFO_FUNCTION(curl);
typedef struct {
zval func_name;
zend_fcall_info_cache fci_cache;
zend_fcall_info_cache fcc;
FILE *fp;
smart_str buf;
int method;
@@ -54,29 +53,23 @@ typedef struct {
} php_curl_write;
typedef struct {
zval func_name;
zend_fcall_info_cache fci_cache;
zend_fcall_info_cache fcc;
FILE *fp;
zend_resource *res;
int method;
zval stream;
} php_curl_read;
typedef struct {
zval func_name;
zend_fcall_info_cache fci_cache;
} php_curl_callback;
typedef struct {
php_curl_write *write;
php_curl_write *write_header;
php_curl_read *read;
zval std_err;
php_curl_callback *progress;
php_curl_callback *xferinfo;
php_curl_callback *fnmatch;
zend_fcall_info_cache progress;
zend_fcall_info_cache xferinfo;
zend_fcall_info_cache fnmatch;
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
php_curl_callback *sshhostkey;
zend_fcall_info_cache sshhostkey;
#endif
} php_curl_handlers;
@@ -114,7 +107,7 @@ typedef struct {
#define CURLOPT_SAFE_UPLOAD -1
typedef struct {
php_curl_callback *server_push;
zend_fcall_info_cache server_push;
} php_curlm_handlers;
typedef struct {

View File

@@ -474,35 +474,41 @@ static HashTable *curl_get_gc(zend_object *object, zval **table, int *n)
zend_get_gc_buffer_add_zval(gc_buffer, &curl->postfields);
if (curl->handlers.read) {
zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.read->func_name);
if (ZEND_FCC_INITIALIZED(curl->handlers.read->fcc)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.read->fcc);
}
zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.read->stream);
}
if (curl->handlers.write) {
zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write->func_name);
if (ZEND_FCC_INITIALIZED(curl->handlers.write->fcc)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.write->fcc);
}
zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write->stream);
}
if (curl->handlers.write_header) {
zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write_header->func_name);
if (ZEND_FCC_INITIALIZED(curl->handlers.write_header->fcc)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.write_header->fcc);
}
zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write_header->stream);
}
if (curl->handlers.progress) {
zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.progress->func_name);
if (ZEND_FCC_INITIALIZED(curl->handlers.progress)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.progress);
}
if (curl->handlers.xferinfo) {
zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.xferinfo->func_name);
if (ZEND_FCC_INITIALIZED(curl->handlers.xferinfo)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.xferinfo);
}
if (curl->handlers.fnmatch) {
zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.fnmatch->func_name);
if (ZEND_FCC_INITIALIZED(curl->handlers.fnmatch)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.fnmatch);
}
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
if (curl->handlers.sshhostkey) {
zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.sshhostkey->func_name);
if (ZEND_FCC_INITIALIZED(curl->handlers.sshhostkey)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.sshhostkey);
}
#endif
@@ -550,20 +556,11 @@ PHP_MSHUTDOWN_FUNCTION(curl)
}
/* }}} */
/* {{{ curl_write_nothing
* Used as a work around. See _php_curl_close_ex
*/
static size_t curl_write_nothing(char *data, size_t size, size_t nmemb, void *ctx)
{
return size * nmemb;
}
/* }}} */
/* {{{ curl_write */
static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx)
{
php_curl *ch = (php_curl *) ctx;
php_curl_write *t = ch->handlers.write;
php_curl_write *write_handler = ch->handlers.write;
size_t length = size * nmemb;
#if PHP_CURL_DEBUG
@@ -571,43 +568,31 @@ static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx)
fprintf(stderr, "data = %s, size = %d, nmemb = %d, ctx = %x\n", data, size, nmemb, ctx);
#endif
switch (t->method) {
switch (write_handler->method) {
case PHP_CURL_STDOUT:
PHPWRITE(data, length);
break;
case PHP_CURL_FILE:
return fwrite(data, size, nmemb, t->fp);
return fwrite(data, size, nmemb, write_handler->fp);
case PHP_CURL_RETURN:
if (length > 0) {
smart_str_appendl(&t->buf, data, (int) length);
smart_str_appendl(&write_handler->buf, data, (int) length);
}
break;
case PHP_CURL_USER: {
zval argv[2];
zval retval;
int error;
zend_fcall_info fci;
GC_ADDREF(&ch->std);
ZVAL_OBJ(&argv[0], &ch->std);
ZVAL_STRINGL(&argv[1], data, length);
fci.size = sizeof(fci);
fci.object = NULL;
ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);
fci.retval = &retval;
fci.param_count = 2;
fci.params = argv;
fci.named_params = NULL;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
ch->in_callback = 0;
if (error == FAILURE) {
php_error_docref(NULL, E_WARNING, "Could not call the CURLOPT_WRITEFUNCTION");
length = -1;
} else if (!Z_ISUNDEF(retval)) {
ch->in_callback = true;
zend_call_known_fcc(&write_handler->fcc, &retval, /* param_count */ 2, argv, /* named_params */ NULL);
ch->in_callback = false;
if (!Z_ISUNDEF(retval)) {
_php_curl_verify_handlers(ch, /* reporterror */ true);
/* TODO Check callback returns an int or something castable to int */
length = zval_get_long(&retval);
}
@@ -625,33 +610,22 @@ static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx)
static int curl_fnmatch(void *ctx, const char *pattern, const char *string)
{
php_curl *ch = (php_curl *) ctx;
php_curl_callback *t = ch->handlers.fnmatch;
int rval = CURL_FNMATCHFUNC_FAIL;
zval argv[3];
zval retval;
zend_result error;
zend_fcall_info fci;
GC_ADDREF(&ch->std);
ZVAL_OBJ(&argv[0], &ch->std);
ZVAL_STRING(&argv[1], pattern);
ZVAL_STRING(&argv[2], string);
fci.size = sizeof(fci);
ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);
fci.object = NULL;
fci.retval = &retval;
fci.param_count = 3;
fci.params = argv;
fci.named_params = NULL;
ch->in_callback = true;
zend_call_known_fcc(&ch->handlers.fnmatch, &retval, /* param_count */ 3, argv, /* named_params */ NULL);
ch->in_callback = false;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
ch->in_callback = 0;
if (error == FAILURE) {
php_error_docref(NULL, E_WARNING, "Cannot call the CURLOPT_FNMATCH_FUNCTION");
} else if (!Z_ISUNDEF(retval)) {
if (!Z_ISUNDEF(retval)) {
_php_curl_verify_handlers(ch, /* reporterror */ true);
/* TODO Check callback returns an int or something castable to int */
rval = zval_get_long(&retval);
}
zval_ptr_dtor(&argv[0]);
@@ -665,7 +639,6 @@ static int curl_fnmatch(void *ctx, const char *pattern, const char *string)
static size_t curl_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
{
php_curl *ch = (php_curl *)clientp;
php_curl_callback *t = ch->handlers.progress;
size_t rval = 0;
#if PHP_CURL_DEBUG
@@ -673,38 +646,29 @@ static size_t curl_progress(void *clientp, double dltotal, double dlnow, double
fprintf(stderr, "clientp = %x, dltotal = %f, dlnow = %f, ultotal = %f, ulnow = %f\n", clientp, dltotal, dlnow, ultotal, ulnow);
#endif
zval argv[5];
zval args[5];
zval retval;
zend_result error;
zend_fcall_info fci;
GC_ADDREF(&ch->std);
ZVAL_OBJ(&argv[0], &ch->std);
ZVAL_LONG(&argv[1], (zend_long)dltotal);
ZVAL_LONG(&argv[2], (zend_long)dlnow);
ZVAL_LONG(&argv[3], (zend_long)ultotal);
ZVAL_LONG(&argv[4], (zend_long)ulnow);
ZVAL_OBJ(&args[0], &ch->std);
ZVAL_LONG(&args[1], (zend_long)dltotal);
ZVAL_LONG(&args[2], (zend_long)dlnow);
ZVAL_LONG(&args[3], (zend_long)ultotal);
ZVAL_LONG(&args[4], (zend_long)ulnow);
fci.size = sizeof(fci);
ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);
fci.object = NULL;
fci.retval = &retval;
fci.param_count = 5;
fci.params = argv;
fci.named_params = NULL;
ch->in_callback = true;
zend_call_known_fcc(&ch->handlers.progress, &retval, /* param_count */ 5, args, /* named_params */ NULL);
ch->in_callback = false;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
ch->in_callback = 0;
if (error == FAILURE) {
php_error_docref(NULL, E_WARNING, "Cannot call the CURLOPT_PROGRESSFUNCTION");
} else if (!Z_ISUNDEF(retval)) {
if (!Z_ISUNDEF(retval)) {
_php_curl_verify_handlers(ch, /* reporterror */ true);
/* TODO Check callback returns an int or something castable to int */
if (0 != zval_get_long(&retval)) {
rval = 1;
}
}
zval_ptr_dtor(&argv[0]);
zval_ptr_dtor(&args[0]);
return rval;
}
/* }}} */
@@ -713,7 +677,6 @@ static size_t curl_progress(void *clientp, double dltotal, double dlnow, double
static size_t curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
{
php_curl *ch = (php_curl *)clientp;
php_curl_callback *t = ch->handlers.xferinfo;
size_t rval = 0;
#if PHP_CURL_DEBUG
@@ -723,8 +686,6 @@ static size_t curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow,
zval argv[5];
zval retval;
zend_result error;
zend_fcall_info fci;
GC_ADDREF(&ch->std);
ZVAL_OBJ(&argv[0], &ch->std);
@@ -733,25 +694,18 @@ static size_t curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow,
ZVAL_LONG(&argv[3], ultotal);
ZVAL_LONG(&argv[4], ulnow);
fci.size = sizeof(fci);
ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);
fci.object = NULL;
fci.retval = &retval;
fci.param_count = 5;
fci.params = argv;
fci.named_params = NULL;
ch->in_callback = true;
zend_call_known_fcc(&ch->handlers.xferinfo, &retval, /* param_count */ 5, argv, /* named_params */ NULL);
ch->in_callback = false;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
ch->in_callback = 0;
if (error == FAILURE) {
php_error_docref(NULL, E_WARNING, "Cannot call the CURLOPT_XFERINFOFUNCTION");
} else if (!Z_ISUNDEF(retval)) {
if (!Z_ISUNDEF(retval)) {
_php_curl_verify_handlers(ch, /* reporterror */ true);
/* TODO Check callback returns an int or something castable to int */
if (0 != zval_get_long(&retval)) {
rval = 1;
}
}
zval_ptr_dtor(&argv[0]);
return rval;
}
@@ -761,7 +715,6 @@ static size_t curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow,
static int curl_ssh_hostkeyfunction(void *clientp, int keytype, const char *key, size_t keylen)
{
php_curl *ch = (php_curl *)clientp;
php_curl_callback *t = ch->handlers.sshhostkey;
int rval = CURLKHMATCH_MISMATCH; /* cancel connection in case of an exception */
#if PHP_CURL_DEBUG
@@ -769,31 +722,20 @@ static int curl_ssh_hostkeyfunction(void *clientp, int keytype, const char *key,
fprintf(stderr, "clientp = %x, keytype = %d, key = %s, keylen = %zu\n", clientp, keytype, key, keylen);
#endif
zval argv[4];
zval args[4];
zval retval;
zend_result error;
zend_fcall_info fci;
GC_ADDREF(&ch->std);
ZVAL_OBJ(&argv[0], &ch->std);
ZVAL_LONG(&argv[1], keytype);
ZVAL_STRINGL(&argv[2], key, keylen);
ZVAL_LONG(&argv[3], keylen);
ZVAL_OBJ(&args[0], &ch->std);
ZVAL_LONG(&args[1], keytype);
ZVAL_STRINGL(&args[2], key, keylen);
ZVAL_LONG(&args[3], keylen);
fci.size = sizeof(fci);
ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);
fci.object = NULL;
fci.retval = &retval;
fci.param_count = 4;
fci.params = argv;
fci.named_params = NULL;
ch->in_callback = true;
zend_call_known_fcc(&ch->handlers.sshhostkey, &retval, /* param_count */ 4, args, /* named_params */ NULL);
ch->in_callback = false;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
ch->in_callback = 0;
if (error == FAILURE) {
php_error_docref(NULL, E_WARNING, "Cannot call the CURLOPT_SSH_HOSTKEYFUNCTION");
} else if (!Z_ISUNDEF(retval)) {
if (!Z_ISUNDEF(retval)) {
_php_curl_verify_handlers(ch, /* reporterror */ true);
if (Z_TYPE(retval) == IS_LONG) {
zend_long retval_long = Z_LVAL(retval);
@@ -806,8 +748,9 @@ static int curl_ssh_hostkeyfunction(void *clientp, int keytype, const char *key,
zend_throw_error(NULL, "The CURLOPT_SSH_HOSTKEYFUNCTION callback must return either CURLKHMATCH_OK or CURLKHMATCH_MISMATCH");
}
}
zval_ptr_dtor(&argv[0]);
zval_ptr_dtor(&argv[2]);
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&args[2]);
return rval;
}
#endif
@@ -816,46 +759,33 @@ static int curl_ssh_hostkeyfunction(void *clientp, int keytype, const char *key,
static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx)
{
php_curl *ch = (php_curl *)ctx;
php_curl_read *t = ch->handlers.read;
php_curl_read *read_handler = ch->handlers.read;
int length = 0;
switch (t->method) {
switch (read_handler->method) {
case PHP_CURL_DIRECT:
if (t->fp) {
length = fread(data, size, nmemb, t->fp);
if (read_handler->fp) {
length = fread(data, size, nmemb, read_handler->fp);
}
break;
case PHP_CURL_USER: {
zval argv[3];
zval retval;
zend_result error;
zend_fcall_info fci;
GC_ADDREF(&ch->std);
ZVAL_OBJ(&argv[0], &ch->std);
if (t->res) {
GC_ADDREF(t->res);
ZVAL_RES(&argv[1], t->res);
if (read_handler->res) {
GC_ADDREF(read_handler->res);
ZVAL_RES(&argv[1], read_handler->res);
} else {
ZVAL_NULL(&argv[1]);
}
ZVAL_LONG(&argv[2], (int)size * nmemb);
fci.size = sizeof(fci);
ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);
fci.object = NULL;
fci.retval = &retval;
fci.param_count = 3;
fci.params = argv;
fci.named_params = NULL;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
ch->in_callback = 0;
if (error == FAILURE) {
php_error_docref(NULL, E_WARNING, "Cannot call the CURLOPT_READFUNCTION");
length = CURL_READFUNC_ABORT;
} else if (!Z_ISUNDEF(retval)) {
ch->in_callback = true;
zend_call_known_fcc(&read_handler->fcc, &retval, /* param_count */ 3, argv, /* named_params */ NULL);
ch->in_callback = false;
if (!Z_ISUNDEF(retval)) {
_php_curl_verify_handlers(ch, /* reporterror */ true);
if (Z_TYPE(retval) == IS_STRING) {
length = MIN((int) (size * nmemb), Z_STRLEN(retval));
@@ -863,6 +793,7 @@ static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx)
} else if (Z_TYPE(retval) == IS_LONG) {
length = Z_LVAL_P(&retval);
}
// TODO Do type error if invalid type?
zval_ptr_dtor(&retval);
}
@@ -880,10 +811,10 @@ static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx)
static size_t curl_write_header(char *data, size_t size, size_t nmemb, void *ctx)
{
php_curl *ch = (php_curl *) ctx;
php_curl_write *t = ch->handlers.write_header;
php_curl_write *write_handler = ch->handlers.write_header;
size_t length = size * nmemb;
switch (t->method) {
switch (write_handler->method) {
case PHP_CURL_STDOUT:
/* Handle special case write when we're returning the entire transfer
*/
@@ -894,32 +825,20 @@ static size_t curl_write_header(char *data, size_t size, size_t nmemb, void *ctx
}
break;
case PHP_CURL_FILE:
return fwrite(data, size, nmemb, t->fp);
return fwrite(data, size, nmemb, write_handler->fp);
case PHP_CURL_USER: {
zval argv[2];
zval retval;
zend_result error;
zend_fcall_info fci;
GC_ADDREF(&ch->std);
ZVAL_OBJ(&argv[0], &ch->std);
ZVAL_STRINGL(&argv[1], data, length);
fci.size = sizeof(fci);
ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);
fci.object = NULL;
fci.retval = &retval;
fci.param_count = 2;
fci.params = argv;
fci.named_params = NULL;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
ch->in_callback = 0;
if (error == FAILURE) {
php_error_docref(NULL, E_WARNING, "Could not call the CURLOPT_HEADERFUNCTION");
length = -1;
} else if (!Z_ISUNDEF(retval)) {
ch->in_callback = true;
zend_call_known_fcc(&write_handler->fcc, &retval, /* param_count */ 2, argv, /* named_params */ NULL);
ch->in_callback = false;
if (!Z_ISUNDEF(retval)) {
// TODO: Check for valid int type for return value
_php_curl_verify_handlers(ch, /* reporterror */ true);
length = zval_get_long(&retval);
}
@@ -1004,8 +923,8 @@ PHP_FUNCTION(curl_version)
/* Add an array of features */
{
struct feat {
const char *name;
int bitmask;
const char *name;
int bitmask;
};
unsigned int i;
@@ -1055,7 +974,7 @@ PHP_FUNCTION(curl_version)
#endif
};
for(i = 0; i < sizeof(feats) / sizeof(feats[0]); i++) {
for(i = 0; i < sizeof(feats) / sizeof(feats[0]); i++) {
if (feats[i].name) {
add_assoc_bool(&feature_list, feats[i].name, d->features & feats[i].bitmask ? true : false);
}
@@ -1117,11 +1036,11 @@ void init_curl_handle(php_curl *ch)
ch->handlers.write = ecalloc(1, sizeof(php_curl_write));
ch->handlers.write_header = ecalloc(1, sizeof(php_curl_write));
ch->handlers.read = ecalloc(1, sizeof(php_curl_read));
ch->handlers.progress = NULL;
ch->handlers.xferinfo = NULL;
ch->handlers.fnmatch = NULL;
ch->handlers.progress = empty_fcall_info_cache;
ch->handlers.xferinfo = empty_fcall_info_cache;
ch->handlers.fnmatch = empty_fcall_info_cache;
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
ch->handlers.sshhostkey = NULL;
ch->handlers.sshhostkey = empty_fcall_info_cache;
#endif
ch->clone = emalloc(sizeof(uint32_t));
*ch->clone = 1;
@@ -1243,13 +1162,10 @@ PHP_FUNCTION(curl_init)
}
/* }}} */
static void _php_copy_callback(php_curl *ch, php_curl_callback **new_callback, php_curl_callback *source_callback, CURLoption option)
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)
{
if (source_callback) {
*new_callback = ecalloc(1, sizeof(php_curl_callback));
if (!Z_ISUNDEF(source_callback->func_name)) {
ZVAL_COPY(&(*new_callback)->func_name, &source_callback->func_name);
}
if (ZEND_FCC_INITIALIZED(*source_fcc)) {
zend_fcc_dup(target_fcc, source_fcc);
curl_easy_setopt(ch->cp, option, (void *) ch);
}
}
@@ -1277,14 +1193,14 @@ void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source)
ch->handlers.read->fp = source->handlers.read->fp;
ch->handlers.read->res = source->handlers.read->res;
if (!Z_ISUNDEF(source->handlers.write->func_name)) {
ZVAL_COPY(&ch->handlers.write->func_name, &source->handlers.write->func_name);
if (ZEND_FCC_INITIALIZED(source->handlers.read->fcc)) {
zend_fcc_dup(&source->handlers.read->fcc, &source->handlers.read->fcc);
}
if (!Z_ISUNDEF(source->handlers.read->func_name)) {
ZVAL_COPY(&ch->handlers.read->func_name, &source->handlers.read->func_name);
if (ZEND_FCC_INITIALIZED(source->handlers.write->fcc)) {
zend_fcc_dup(&source->handlers.write->fcc, &source->handlers.write->fcc);
}
if (!Z_ISUNDEF(source->handlers.write_header->func_name)) {
ZVAL_COPY(&ch->handlers.write_header->func_name, &source->handlers.write_header->func_name);
if (ZEND_FCC_INITIALIZED(source->handlers.write_header->fcc)) {
zend_fcc_dup(&source->handlers.write_header->fcc, &source->handlers.write_header->fcc);
}
curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str);
@@ -1293,11 +1209,11 @@ void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source)
curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch);
curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *) ch);
_php_copy_callback(ch, &ch->handlers.progress, source->handlers.progress, CURLOPT_PROGRESSDATA);
_php_copy_callback(ch, &ch->handlers.xferinfo, source->handlers.xferinfo, CURLOPT_XFERINFODATA);
_php_copy_callback(ch, &ch->handlers.fnmatch, source->handlers.fnmatch, CURLOPT_FNMATCH_DATA);
php_curl_copy_fcc_with_option(ch, CURLOPT_PROGRESSDATA, &ch->handlers.progress, &source->handlers.progress);
php_curl_copy_fcc_with_option(ch, CURLOPT_XFERINFODATA, &ch->handlers.xferinfo, &source->handlers.xferinfo);
php_curl_copy_fcc_with_option(ch, CURLOPT_FNMATCH_DATA, &ch->handlers.fnmatch, &source->handlers.fnmatch);
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
_php_copy_callback(ch, &ch->handlers.sshhostkey, source->handlers.sshhostkey, CURLOPT_SSH_HOSTKEYDATA);
php_curl_copy_fcc_with_option(ch, CURLOPT_SSH_HOSTKEYDATA, &ch->handlers.sshhostkey, &source->handlers.sshhostkey);
#endif
ZVAL_COPY(&ch->private_data, &source->private_data);
@@ -1598,12 +1514,64 @@ PHP_FUNCTION(curl_copy_handle)
}
/* }}} */
static bool php_curl_set_callable_handler(zend_fcall_info_cache *const handler_fcc, zval *callable, bool is_array_config, const char *option_name)
{
if (ZEND_FCC_INITIALIZED(*handler_fcc)) {
zend_fcc_dtor(handler_fcc);
}
char *error = NULL;
if (UNEXPECTED(!zend_is_callable_ex(callable, /* object */ NULL, /* check_flags */ 0, /* callable_name */ NULL, handler_fcc, /* error */ &error))) {
if (!EG(exception)) {
zend_argument_type_error(2 + !is_array_config, "must be a valid callback for option %s, %s", option_name, error);
}
efree(error);
return false;
}
zend_fcc_addref(handler_fcc);
return true;
}
#define HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(curl_ptr, constant_no_function, handler_type) \
case constant_no_function##FUNCTION: { \
bool result = php_curl_set_callable_handler(&curl_ptr->handlers.handler_type->fcc, zvalue, is_array_config, #constant_no_function "FUNCTION"); \
if (!result) { \
return FAILURE; \
} \
curl_ptr->handlers.handler_type->method = PHP_CURL_USER; \
break; \
}
#define HANDLE_CURL_OPTION_CALLABLE(curl_ptr, constant_no_function, handler_fcc, c_callback) \
case constant_no_function##FUNCTION: { \
bool result = php_curl_set_callable_handler(&curl_ptr->handler_fcc, zvalue, is_array_config, #constant_no_function "FUNCTION"); \
if (!result) { \
return FAILURE; \
} \
curl_easy_setopt(curl_ptr->cp, constant_no_function##FUNCTION, (c_callback)); \
curl_easy_setopt(curl_ptr->cp, constant_no_function##DATA, curl_ptr); \
break; \
}
static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool is_array_config) /* {{{ */
{
CURLcode error = CURLE_OK;
zend_long lval;
switch (option) {
/* Callable options */
HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_WRITE, write);
HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_HEADER, write_header);
HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_READ, read);
HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PROGRESS, handlers.progress, curl_progress);
HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_XFERINFO, handlers.xferinfo, curl_xferinfo);
HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_FNMATCH_, handlers.fnmatch, curl_fnmatch);
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_SSH_HOSTKEY, handlers.sshhostkey, curl_ssh_hostkeyfunction);
#endif
/* Long options */
case CURLOPT_SSL_VERIFYHOST:
lval = zval_get_long(zvalue);
@@ -2113,15 +2081,6 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
error = curl_easy_setopt(ch->cp, option, lval);
break;
case CURLOPT_HEADERFUNCTION:
if (!Z_ISUNDEF(ch->handlers.write_header->func_name)) {
zval_ptr_dtor(&ch->handlers.write_header->func_name);
ch->handlers.write_header->fci_cache = empty_fcall_info_cache;
}
ZVAL_COPY(&ch->handlers.write_header->func_name, zvalue);
ch->handlers.write_header->method = PHP_CURL_USER;
break;
case CURLOPT_POSTFIELDS:
if (Z_TYPE_P(zvalue) == IS_ARRAY) {
if (zend_hash_num_elements(HASH_OF(zvalue)) == 0) {
@@ -2142,41 +2101,6 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
}
break;
case CURLOPT_PROGRESSFUNCTION:
curl_easy_setopt(ch->cp, CURLOPT_PROGRESSFUNCTION, curl_progress);
curl_easy_setopt(ch->cp, CURLOPT_PROGRESSDATA, ch);
if (ch->handlers.progress == NULL) {
ch->handlers.progress = ecalloc(1, sizeof(php_curl_callback));
} else if (!Z_ISUNDEF(ch->handlers.progress->func_name)) {
zval_ptr_dtor(&ch->handlers.progress->func_name);
ch->handlers.progress->fci_cache = empty_fcall_info_cache;
}
ZVAL_COPY(&ch->handlers.progress->func_name, zvalue);
break;
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
case CURLOPT_SSH_HOSTKEYFUNCTION:
curl_easy_setopt(ch->cp, CURLOPT_SSH_HOSTKEYFUNCTION, curl_ssh_hostkeyfunction);
curl_easy_setopt(ch->cp, CURLOPT_SSH_HOSTKEYDATA, ch);
if (ch->handlers.sshhostkey == NULL) {
ch->handlers.sshhostkey = ecalloc(1, sizeof(php_curl_callback));
} else if (!Z_ISUNDEF(ch->handlers.sshhostkey->func_name)) {
zval_ptr_dtor(&ch->handlers.sshhostkey->func_name);
ch->handlers.sshhostkey->fci_cache = empty_fcall_info_cache;
}
ZVAL_COPY(&ch->handlers.sshhostkey->func_name, zvalue);
break;
#endif
case CURLOPT_READFUNCTION:
if (!Z_ISUNDEF(ch->handlers.read->func_name)) {
zval_ptr_dtor(&ch->handlers.read->func_name);
ch->handlers.read->fci_cache = empty_fcall_info_cache;
}
ZVAL_COPY(&ch->handlers.read->func_name, zvalue);
ch->handlers.read->method = PHP_CURL_USER;
break;
case CURLOPT_RETURNTRANSFER:
if (zend_is_true(zvalue)) {
ch->handlers.write->method = PHP_CURL_RETURN;
@@ -2185,27 +2109,6 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
}
break;
case CURLOPT_WRITEFUNCTION:
if (!Z_ISUNDEF(ch->handlers.write->func_name)) {
zval_ptr_dtor(&ch->handlers.write->func_name);
ch->handlers.write->fci_cache = empty_fcall_info_cache;
}
ZVAL_COPY(&ch->handlers.write->func_name, zvalue);
ch->handlers.write->method = PHP_CURL_USER;
break;
case CURLOPT_XFERINFOFUNCTION:
curl_easy_setopt(ch->cp, CURLOPT_XFERINFOFUNCTION, curl_xferinfo);
curl_easy_setopt(ch->cp, CURLOPT_XFERINFODATA, ch);
if (ch->handlers.xferinfo == NULL) {
ch->handlers.xferinfo = ecalloc(1, sizeof(php_curl_callback));
} else if (!Z_ISUNDEF(ch->handlers.xferinfo->func_name)) {
zval_ptr_dtor(&ch->handlers.xferinfo->func_name);
ch->handlers.xferinfo->fci_cache = empty_fcall_info_cache;
}
ZVAL_COPY(&ch->handlers.xferinfo->func_name, zvalue);
break;
/* Curl off_t options */
case CURLOPT_MAX_RECV_SPEED_LARGE:
case CURLOPT_MAX_SEND_SPEED_LARGE:
@@ -2234,7 +2137,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
case CURLOPT_ISSUERCERT:
case CURLOPT_SSH_KNOWNHOSTS:
{
zend_string *tmp_str;
zend_string *tmp_str;
zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);
zend_result ret;
@@ -2275,18 +2178,6 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
}
break;
case CURLOPT_FNMATCH_FUNCTION:
curl_easy_setopt(ch->cp, CURLOPT_FNMATCH_FUNCTION, curl_fnmatch);
curl_easy_setopt(ch->cp, CURLOPT_FNMATCH_DATA, ch);
if (ch->handlers.fnmatch == NULL) {
ch->handlers.fnmatch = ecalloc(1, sizeof(php_curl_callback));
} else if (!Z_ISUNDEF(ch->handlers.fnmatch->func_name)) {
zval_ptr_dtor(&ch->handlers.fnmatch->func_name);
ch->handlers.fnmatch->fci_cache = empty_fcall_info_cache;
}
ZVAL_COPY(&ch->handlers.fnmatch->func_name, zvalue);
break;
/* Curl blob options */
#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */
case CURLOPT_ISSUERCERT_BLOB:
@@ -2775,14 +2666,6 @@ PHP_FUNCTION(curl_close)
}
/* }}} */
static void _php_curl_free_callback(php_curl_callback* callback)
{
if (callback) {
zval_ptr_dtor(&callback->func_name);
efree(callback);
}
}
static void curl_free_obj(zend_object *object)
{
php_curl *ch = curl_from_obj(object);
@@ -2799,20 +2682,6 @@ static void curl_free_obj(zend_object *object)
_php_curl_verify_handlers(ch, /* reporterror */ false);
/*
* Libcurl is doing connection caching. When easy handle is cleaned up,
* if the handle was previously used by the curl_multi_api, the connection
* remains open un the curl multi handle is cleaned up. Some protocols are
* sending content like the FTP one, and libcurl try to use the
* WRITEFUNCTION or the HEADERFUNCTION. Since structures used in those
* callback are freed, we need to use an other callback to which avoid
* segfaults.
*
* Libcurl commit d021f2e8a00 fix this issue and should be part of 7.28.2
*/
curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, curl_write_nothing);
curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, curl_write_nothing);
curl_easy_cleanup(ch->cp);
/* cURL destructors should be invoked only by last curl handle */
@@ -2827,9 +2696,15 @@ static void curl_free_obj(zend_object *object)
}
smart_str_free(&ch->handlers.write->buf);
zval_ptr_dtor(&ch->handlers.write->func_name);
zval_ptr_dtor(&ch->handlers.read->func_name);
zval_ptr_dtor(&ch->handlers.write_header->func_name);
if (ZEND_FCC_INITIALIZED(ch->handlers.write->fcc)) {
zend_fcc_dtor(&ch->handlers.write->fcc);
}
if (ZEND_FCC_INITIALIZED(ch->handlers.write_header->fcc)) {
zend_fcc_dtor(&ch->handlers.write_header->fcc);
}
if (ZEND_FCC_INITIALIZED(ch->handlers.read->fcc)) {
zend_fcc_dtor(&ch->handlers.read->fcc);
}
zval_ptr_dtor(&ch->handlers.std_err);
if (ch->header.str) {
zend_string_release_ex(ch->header.str, 0);
@@ -2843,11 +2718,19 @@ static void curl_free_obj(zend_object *object)
efree(ch->handlers.write_header);
efree(ch->handlers.read);
_php_curl_free_callback(ch->handlers.progress);
_php_curl_free_callback(ch->handlers.xferinfo);
_php_curl_free_callback(ch->handlers.fnmatch);
if (ZEND_FCC_INITIALIZED(ch->handlers.progress)) {
zend_fcc_dtor(&ch->handlers.progress);
}
if (ZEND_FCC_INITIALIZED(ch->handlers.xferinfo)) {
zend_fcc_dtor(&ch->handlers.xferinfo);
}
if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) {
zend_fcc_dtor(&ch->handlers.fnmatch);
}
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
_php_curl_free_callback(ch->handlers.sshhostkey);
if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) {
zend_fcc_dtor(&ch->handlers.sshhostkey);
}
#endif
zval_ptr_dtor(&ch->postfields);
@@ -2911,29 +2794,21 @@ static void _php_curl_reset_handlers(php_curl *ch)
ZVAL_UNDEF(&ch->handlers.std_err);
}
if (ch->handlers.progress) {
zval_ptr_dtor(&ch->handlers.progress->func_name);
efree(ch->handlers.progress);
ch->handlers.progress = NULL;
if (ZEND_FCC_INITIALIZED(ch->handlers.progress)) {
zend_fcc_dtor(&ch->handlers.progress);
}
if (ch->handlers.xferinfo) {
zval_ptr_dtor(&ch->handlers.xferinfo->func_name);
efree(ch->handlers.xferinfo);
ch->handlers.xferinfo = NULL;
if (ZEND_FCC_INITIALIZED(ch->handlers.xferinfo)) {
zend_fcc_dtor(&ch->handlers.xferinfo);
}
if (ch->handlers.fnmatch) {
zval_ptr_dtor(&ch->handlers.fnmatch->func_name);
efree(ch->handlers.fnmatch);
ch->handlers.fnmatch = NULL;
if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) {
zend_fcc_dtor(&ch->handlers.fnmatch);
}
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
if (ch->handlers.sshhostkey) {
zval_ptr_dtor(&ch->handlers.sshhostkey->func_name);
efree(ch->handlers.sshhostkey);
ch->handlers.sshhostkey = NULL;
if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) {
zend_fcc_dtor(&ch->handlers.sshhostkey);
}
#endif
}

View File

@@ -372,51 +372,35 @@ static int _php_server_push_callback(CURL *parent_ch, CURL *easy, size_t num_hea
php_curl *parent;
php_curlm *mh = (php_curlm *)userp;
size_t rval = CURL_PUSH_DENY;
php_curl_callback *t = mh->handlers.server_push;
zval *pz_parent_ch = NULL;
zval pz_ch;
zval headers;
zval retval;
char *header;
zend_result error;
zend_fcall_info fci = empty_fcall_info;
pz_parent_ch = _php_curl_multi_find_easy_handle(mh, parent_ch);
if (pz_parent_ch == NULL) {
return rval;
}
if (UNEXPECTED(zend_fcall_info_init(&t->func_name, 0, &fci, &t->fci_cache, NULL, NULL) == FAILURE)) {
php_error_docref(NULL, E_WARNING, "Cannot call the CURLMOPT_PUSHFUNCTION");
return rval;
}
parent = Z_CURL_P(pz_parent_ch);
ch = init_curl_handle_into_zval(&pz_ch);
ch->cp = easy;
_php_setup_easy_copy_handlers(ch, parent);
size_t i;
array_init(&headers);
for(i=0; i<num_headers; i++) {
header = curl_pushheader_bynum(push_headers, i);
for (size_t i = 0; i < num_headers; i++) {
char *header = curl_pushheader_bynum(push_headers, i);
add_next_index_string(&headers, header);
}
ZEND_ASSERT(pz_parent_ch);
zval call_args[3] = {*pz_parent_ch, pz_ch, headers};
fci.param_count = 3;
fci.params = call_args;
fci.retval = &retval;
error = zend_call_function(&fci, &t->fci_cache);
zend_call_known_fcc(&mh->handlers.server_push, &retval, /* param_count */ 3, call_args, /* named_params */ NULL);
zval_ptr_dtor_nogc(&headers);
if (error == FAILURE) {
php_error_docref(NULL, E_WARNING, "Cannot call the CURLMOPT_PUSHFUNCTION");
} else if (!Z_ISUNDEF(retval)) {
if (!Z_ISUNDEF(retval)) {
if (CURL_PUSH_DENY != zval_get_long(&retval)) {
rval = CURL_PUSH_OK;
zend_llist_add_element(&mh->easyh, &pz_ch);
@@ -458,21 +442,29 @@ static bool _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue
error = curl_multi_setopt(mh->multi, option, lval);
break;
}
case CURLMOPT_PUSHFUNCTION:
if (mh->handlers.server_push == NULL) {
mh->handlers.server_push = ecalloc(1, sizeof(php_curl_callback));
} else if (!Z_ISUNDEF(mh->handlers.server_push->func_name)) {
zval_ptr_dtor(&mh->handlers.server_push->func_name);
mh->handlers.server_push->fci_cache = empty_fcall_info_cache;
case CURLMOPT_PUSHFUNCTION: {
/* See php_curl_set_callable_handler */
if (ZEND_FCC_INITIALIZED(mh->handlers.server_push)) {
zend_fcc_dtor(&mh->handlers.server_push);
}
ZVAL_COPY(&mh->handlers.server_push->func_name, zvalue);
char *error_str = NULL;
if (UNEXPECTED(!zend_is_callable_ex(zvalue, /* object */ NULL, /* check_flags */ 0, /* callable_name */ NULL, &mh->handlers.server_push, /* error */ &error_str))) {
if (!EG(exception)) {
zend_argument_type_error(2, "must be a valid callback for option CURLMOPT_PUSHFUNCTION, %s", error_str);
}
efree(error_str);
return false;
}
zend_fcc_addref(&mh->handlers.server_push);
error = curl_multi_setopt(mh->multi, CURLMOPT_PUSHFUNCTION, _php_server_push_callback);
if (error != CURLM_OK) {
return false;
}
error = curl_multi_setopt(mh->multi, CURLMOPT_PUSHDATA, mh);
break;
}
default:
zend_argument_value_error(2, "is not a valid cURL multi option");
error = CURLM_UNKNOWN_OPTION;
@@ -548,9 +540,9 @@ static void curl_multi_free_obj(zend_object *object)
curl_multi_cleanup(mh->multi);
zend_llist_clean(&mh->easyh);
if (mh->handlers.server_push) {
zval_ptr_dtor(&mh->handlers.server_push->func_name);
efree(mh->handlers.server_push);
if (ZEND_FCC_INITIALIZED(mh->handlers.server_push)) {
zend_fcc_dtor(&mh->handlers.server_push);
}
zend_object_std_dtor(&mh->std);
@@ -562,8 +554,8 @@ static HashTable *curl_multi_get_gc(zend_object *object, zval **table, int *n)
zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
if (curl_multi->handlers.server_push) {
zend_get_gc_buffer_add_zval(gc_buffer, &curl_multi->handlers.server_push->func_name);
if (ZEND_FCC_INITIALIZED(curl_multi->handlers.server_push)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &curl_multi->handlers.server_push);
}
zend_llist_position pos;

View File

@@ -1,26 +0,0 @@
--TEST--
Test curl_copy_handle() with CURLOPT_PROGRESSFUNCTION
--EXTENSIONS--
curl
--FILE--
<?php
include 'server.inc';
$host = curl_cli_server_start();
$url = "{$host}/get.inc";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_NOPROGRESS, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function() { });
$ch2 = curl_copy_handle($ch);
echo curl_exec($ch), PHP_EOL;
unset($ch);
echo curl_exec($ch2);
?>
--EXPECT--
Hello World!
Hello World!
Hello World!
Hello World!

View File

@@ -4,19 +4,23 @@ Test curl_copy_handle() with CURLOPT_XFERINFOFUNCTION
curl
--FILE--
<?php
include 'server.inc';
$host = curl_cli_server_start();
include 'server.inc';
$host = curl_cli_server_start();
$url = "{$host}/get.inc";
$ch = curl_init($url);
$url = "{$host}/get.inc";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_NOPROGRESS, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_XFERINFOFUNCTION, function() { static $done = false; if (!$done) { echo "Download progress!\n"; $done = true; } });
$ch2 = curl_copy_handle($ch);
echo curl_exec($ch), PHP_EOL;
unset($ch);
echo curl_exec($ch2);
function foo() {
static $done = false; if (!$done) { echo "Download progress!\n"; $done = true; }
}
curl_setopt($ch, CURLOPT_NOPROGRESS, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_XFERINFOFUNCTION, 'foo');
$ch2 = curl_copy_handle($ch);
echo curl_exec($ch), PHP_EOL;
unset($ch);
echo curl_exec($ch2);
?>
--EXPECT--

View File

@@ -0,0 +1,34 @@
--TEST--
Test trampoline for curl option CURLOPT_FNMATCH_FUNCTION
--EXTENSIONS--
curl
--SKIPIF--
<?php
exit("skip: cannot properly test CURLOPT_FNMATCH_FUNCTION");
?>
--FILE--
<?php
include 'server.inc';
$host = curl_cli_server_start();
class TrampolineTest {
public function __call(string $name, array $arguments) {
echo 'Trampoline for ', $name, PHP_EOL;
return CURL_FNMATCHFUNC_NOMATCH;
}
}
$o = new TrampolineTest();
$callback = [$o, 'trampoline'];
$url = "ftp://{$host}/file*";
//$url = "ftp://ftp.example.com/file*";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_WILDCARDMATCH, 1);
curl_setopt($ch, CURLOPT_FNMATCH_FUNCTION, $callback);
echo curl_exec($ch), PHP_EOL;
?>
--EXPECT--
Trampoline for trampoline
Hello World!
Hello World!

View File

@@ -0,0 +1,17 @@
--TEST--
Test curl_multi_setopt() with options that take callabes
--EXTENSIONS--
curl
--FILE--
<?php
$mh = curl_multi_init();
try {
var_dump(curl_multi_setopt($mh, CURLMOPT_PUSHFUNCTION, 'undefined'));
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
?>
--EXPECT--
TypeError: curl_multi_setopt(): Argument #2 ($option) must be a valid callback for option CURLMOPT_PUSHFUNCTION, function "undefined" not found or invalid function name

View File

@@ -0,0 +1,25 @@
--TEST--
Test curl_copy_handle() with CURLOPT_PROGRESSFUNCTION
--EXTENSIONS--
curl
--FILE--
<?php
include 'server.inc';
$host = curl_cli_server_start();
$url = "{$host}/get.inc";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_NOPROGRESS, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function() { });
$ch2 = curl_copy_handle($ch);
echo curl_exec($ch), PHP_EOL;
unset($ch);
echo curl_exec($ch2);
?>
--EXPECT--
Hello World!
Hello World!
Hello World!
Hello World!

View File

@@ -0,0 +1,34 @@
--TEST--
Test trampoline for curl option CURLOPT_PROGRESSFUNCTION
--EXTENSIONS--
curl
--FILE--
<?php
include 'server.inc';
$host = curl_cli_server_start();
class TrampolineTest {
public function __call(string $name, array $arguments) {
static $done = false;
if (!$done) {
echo 'Trampoline for ', $name, PHP_EOL;
$done = true;
}
return CURL_PUSH_OK;
}
}
$o = new TrampolineTest();
$callback = [$o, 'trampoline'];
$url = "{$host}/get.inc";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_NOPROGRESS, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, $callback);
echo curl_exec($ch), PHP_EOL;
?>
--EXPECT--
Trampoline for trampoline
Hello World!
Hello World!

View File

@@ -1,9 +1,5 @@
--TEST--
Test CURLMOPT_PUSHFUNCTION with non-existent callback function
--CREDITS--
Davey Shafik
Kévin Dunglas
Niels Dossche
Test trampoline for curl option CURLMOPT_PUSHFUNCTION
--EXTENSIONS--
curl
--SKIPIF--
@@ -17,12 +13,20 @@ if ($curl_version['version_number'] < 0x080100) {
?>
--FILE--
<?php
// Test adapted from curl_pushfunction.phpt
class TrampolineTest {
public function __call(string $name, array $arguments) {
echo 'Trampoline for ', $name, PHP_EOL;
return CURL_PUSH_OK;
}
}
$o = new TrampolineTest();
$callback = [$o, 'trampoline'];
$mh = curl_multi_init();
curl_multi_setopt($mh, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
curl_multi_setopt($mh, CURLMOPT_PUSHFUNCTION, "nonexistent");
curl_multi_setopt($mh, CURLMOPT_PUSHFUNCTION, $callback);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://localhost/serverpush");
@@ -31,8 +35,9 @@ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_multi_add_handle($mh, $ch);
$responses = [];
$active = null;
while(true) {
do {
$status = curl_multi_exec($mh, $active);
do {
@@ -40,15 +45,22 @@ while(true) {
if (false !== $info && $info['msg'] == CURLMSG_DONE) {
$handle = $info['handle'];
if ($handle !== null) {
$responses[] = curl_multi_getcontent($info['handle']);
curl_multi_remove_handle($mh, $handle);
curl_close($handle);
break 2;
}
}
} while ($info);
}
} while (count($responses) !== 2);
curl_multi_close($mh);
sort($responses);
print_r($responses);
?>
--EXPECTF--
Warning: curl_multi_exec(): Cannot call the CURLMOPT_PUSHFUNCTION in %s on line %d
--EXPECT--
Array
(
[0] => main response
[1] => pushed response
)

View File

@@ -0,0 +1,49 @@
--TEST--
Test trampoline for curl option CURLOPT_READFUNCTION
--EXTENSIONS--
curl
--FILE--
<?php
class TrampolineTest {
public function __call(string $name, array $arguments) {
echo 'Trampoline for ', $name, PHP_EOL;
return 0;
}
}
$o = new TrampolineTest();
$callback = [$o, 'trampoline'];
$sFileBase = __DIR__.DIRECTORY_SEPARATOR.'curl_read_trampoline';
$sReadFile = $sFileBase.'_in.tmp';
$sWriteFile = $sFileBase.'_out.tmp';
$sWriteUrl = 'file://'.$sWriteFile;
file_put_contents($sReadFile,'contents of tempfile');
$hReadHandle = fopen($sReadFile, 'r');
$oCurl = curl_init();
curl_setopt($oCurl, CURLOPT_URL, $sWriteUrl);
curl_setopt($oCurl, CURLOPT_UPLOAD, 1);
curl_setopt($oCurl, CURLOPT_READFUNCTION, $callback);
curl_setopt($oCurl, CURLOPT_INFILE, $hReadHandle );
curl_exec($oCurl);
curl_close($oCurl);
fclose ($hReadHandle);
$sOutput = file_get_contents($sWriteFile);
var_dump($sOutput);
?>
--CLEAN--
<?php
$sFileBase = __DIR__.DIRECTORY_SEPARATOR.'curl_read_trampoline';
$sReadFile = $sFileBase.'_in.tmp';
$sWriteFile = $sFileBase.'_out.tmp';
@unlink($sReadFile);
@unlink($sWriteFile);
?>
--EXPECT--
Trampoline for trampoline
string(0) ""

View File

@@ -0,0 +1,44 @@
--TEST--
Test curl_setopt(_array)() with options that take callabes
--EXTENSIONS--
curl
--FILE--
<?php
function testOption(CurlHandle $handle, int $option) {
try {
var_dump(curl_setopt($handle, $option, 'undefined'));
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
try {
var_dump(curl_setopt_array($handle, [$option => 'undefined']));
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
}
$url = "https://example.com";
$ch = curl_init($url);
testOption($ch, CURLOPT_PROGRESSFUNCTION);
testOption($ch, CURLOPT_XFERINFOFUNCTION);
testOption($ch, CURLOPT_FNMATCH_FUNCTION);
testOption($ch, CURLOPT_WRITEFUNCTION);
testOption($ch, CURLOPT_HEADERFUNCTION);
testOption($ch, CURLOPT_READFUNCTION);
?>
--EXPECT--
TypeError: curl_setopt(): Argument #3 ($value) must be a valid callback for option CURLOPT_PROGRESSFUNCTION, function "undefined" not found or invalid function name
TypeError: curl_setopt_array(): Argument #2 ($options) must be a valid callback for option CURLOPT_PROGRESSFUNCTION, function "undefined" not found or invalid function name
TypeError: curl_setopt(): Argument #3 ($value) must be a valid callback for option CURLOPT_XFERINFOFUNCTION, function "undefined" not found or invalid function name
TypeError: curl_setopt_array(): Argument #2 ($options) must be a valid callback for option CURLOPT_XFERINFOFUNCTION, function "undefined" not found or invalid function name
TypeError: curl_setopt(): Argument #3 ($value) must be a valid callback for option CURLOPT_FNMATCH_FUNCTION, function "undefined" not found or invalid function name
TypeError: curl_setopt_array(): Argument #2 ($options) must be a valid callback for option CURLOPT_FNMATCH_FUNCTION, function "undefined" not found or invalid function name
TypeError: curl_setopt(): Argument #3 ($value) must be a valid callback for option CURLOPT_WRITEFUNCTION, function "undefined" not found or invalid function name
TypeError: curl_setopt_array(): Argument #2 ($options) must be a valid callback for option CURLOPT_WRITEFUNCTION, function "undefined" not found or invalid function name
TypeError: curl_setopt(): Argument #3 ($value) must be a valid callback for option CURLOPT_HEADERFUNCTION, function "undefined" not found or invalid function name
TypeError: curl_setopt_array(): Argument #2 ($options) must be a valid callback for option CURLOPT_HEADERFUNCTION, function "undefined" not found or invalid function name
TypeError: curl_setopt(): Argument #3 ($value) must be a valid callback for option CURLOPT_READFUNCTION, function "undefined" not found or invalid function name
TypeError: curl_setopt_array(): Argument #2 ($options) must be a valid callback for option CURLOPT_READFUNCTION, function "undefined" not found or invalid function name

View File

@@ -0,0 +1,36 @@
--TEST--
Test curl_setopt(_array)() with CURLOPT_SSH_HOSTKEYFUNCTION option
--EXTENSIONS--
curl
--SKIPIF--
<?php
$curl_version = curl_version();
if ($curl_version['version_number'] < 0x075400) {
die("skip: blob options not supported for curl < 7.84.0");
}
?>
--FILE--
<?php
function testOption(CurlHandle $handle, int $option) {
try {
var_dump(curl_setopt($handle, $option, 'undefined'));
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
try {
var_dump(curl_setopt_array($handle, [$option => 'undefined']));
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
}
$url = "https://example.com";
$ch = curl_init($url);
testOption($ch, CURLOPT_SSH_HOSTKEYFUNCTION);
?>
--EXPECT--
TypeError: curl_setopt(): Argument #3 ($value) must be a valid callback for option CURLOPT_SSH_HOSTKEYFUNCTION, function "undefined" not found or invalid function name
TypeError: curl_setopt_array(): Argument #2 ($options) must be a valid callback for option CURLOPT_SSH_HOSTKEYFUNCTION, function "undefined" not found or invalid function name

View File

@@ -0,0 +1,41 @@
--TEST--
Test trampoline for curl option CURLOPT_SSH_HOSTKEYFUNCTION
--EXTENSIONS--
curl
--SKIPIF--
<?php
$curl_version = curl_version();
if ($curl_version['version_number'] < 0x075400) {
exit("skip: test works only with curl >= 7.84.0");
}
exit("skip: cannot properly test CURLOPT_SSH_HOSTKEYFUNCTION");
?>
--FILE--
<?php
class TrampolineTest {
public function __call(string $name, array $arguments) {
echo 'Trampoline for ', $name, PHP_EOL;
return CURLKHMATCH_MISMATCH;
}
}
$o = new TrampolineTest();
$callback = [$o, 'trampoline'];
include 'server.inc';
$ch = curl_init();
$host = curl_cli_server_start();
var_dump($host);
//$url = "scp://{$host}/get.inc";
//$url = "scp://example.com/example.txt";
curl_setopt($ch, CURLOPT_URL, $url);
var_dump(curl_setopt($ch, CURLOPT_SSH_HOSTKEYFUNCTION, $callback));
curl_exec($ch);
curl_close($ch);
?>
--EXPECT--
Trampoline for trampoline
FAKE

View File

@@ -0,0 +1,36 @@
--TEST--
Test trampoline for curl option CURLOPT_WRITEFUNCTION
--EXTENSIONS--
curl
--FILE--
<?php
class TrampolineTest {
public function __call(string $name, array $arguments) {
echo 'Trampoline for ', $name, PHP_EOL;
return 0;
}
}
$o = new TrampolineTest();
$callback = [$o, 'trampoline'];
$log_file = tempnam(sys_get_temp_dir(), 'php-curl-CURLOPT_WRITEFUNCTION-trampoline');
$fp = fopen($log_file, 'w+');
fwrite($fp, "test");
fclose($fp);
$ch = curl_init();
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $callback);
curl_setopt($ch, CURLOPT_URL, 'file://' . $log_file);
curl_exec($ch);
curl_close($ch);
?>
--CLEAN--
<?php
$log_file = tempnam(sys_get_temp_dir(), 'php-curl-CURLOPT_WRITEFUNCTION-trampoline');
@unlink($log_file);
?>
--EXPECT--
Trampoline for trampoline

View File

@@ -0,0 +1,28 @@
--TEST--
Test trampoline for curl option CURLOPT_HEADERFUNCTION
--EXTENSIONS--
curl
--FILE--
<?php
class TrampolineTest {
public function __call(string $name, array $arguments) {
echo 'Trampoline for ', $name, PHP_EOL;
return CURL_PUSH_OK;
}
}
$o = new TrampolineTest();
$callback = [$o, 'trampoline'];
include 'server.inc';
$host = curl_cli_server_start();
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADERFUNCTION, $callback);
curl_setopt($ch, CURLOPT_URL, $host);
curl_exec($ch);
?>
--EXPECT--
Trampoline for trampoline

View File

@@ -0,0 +1,34 @@
--TEST--
Test trampoline for curl option CURLOPT_XFERINFOFUNCTION
--EXTENSIONS--
curl
--FILE--
<?php
include 'server.inc';
$host = curl_cli_server_start();
class TrampolineTest {
public function __call(string $name, array $arguments) {
static $done = false;
if (!$done) {
echo 'Trampoline for ', $name, PHP_EOL;
$done = true;
}
return CURL_PUSH_OK;
}
}
$o = new TrampolineTest();
$callback = [$o, 'trampoline'];
$url = "{$host}/get.inc";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_NOPROGRESS, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_XFERINFOFUNCTION, $callback);
echo curl_exec($ch), PHP_EOL;
?>
--EXPECT--
Trampoline for trampoline
Hello World!
Hello World!