Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions ext/curl/curl.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -2713,6 +2713,56 @@
* @cvalue CURLMOPT_PUSHFUNCTION
*/
const CURLMOPT_PUSHFUNCTION = UNKNOWN;
/**
* @var int
* @cvalue CURLMOPT_SOCKETFUNCTION
*/
const CURLMOPT_SOCKETFUNCTION = UNKNOWN;
/**
* @var int
* @cvalue CURLMOPT_TIMERFUNCTION
*/
const CURLMOPT_TIMERFUNCTION = UNKNOWN;
/**
* @var int
* @cvalue CURL_POLL_IN
*/
const CURL_POLL_IN = UNKNOWN;
/**
* @var int
* @cvalue CURL_POLL_OUT
*/
const CURL_POLL_OUT = UNKNOWN;
/**
* @var int
* @cvalue CURL_POLL_INOUT
*/
const CURL_POLL_INOUT = UNKNOWN;
/**
* @var int
* @cvalue CURL_POLL_REMOVE
*/
const CURL_POLL_REMOVE = UNKNOWN;
/**
* @var int
* @cvalue CURL_CSELECT_IN
*/
const CURL_CSELECT_IN = UNKNOWN;
/**
* @var int
* @cvalue CURL_CSELECT_OUT
*/
const CURL_CSELECT_OUT = UNKNOWN;
/**
* @var int
* @cvalue CURL_CSELECT_ERR
*/
const CURL_CSELECT_ERR = UNKNOWN;
/**
* @var int
* @cvalue CURL_SOCKET_TIMEOUT
*/
const CURL_SOCKET_TIMEOUT = UNKNOWN;
/**
* @var int
* @cvalue CURL_PUSH_OK
Expand Down Expand Up @@ -3759,6 +3809,9 @@ function curl_unescape(CurlHandle $handle, string $string): string|false {}

function curl_multi_setopt(CurlMultiHandle $multi_handle, int $option, mixed $value): bool {}

/** @param mixed $socket */
function curl_multi_socket_action(CurlMultiHandle $multi_handle, $socket, int $what, int &$still_running): int {};

function curl_exec(CurlHandle $handle): string|bool {}

/** @refcount 1 */
Expand Down
21 changes: 20 additions & 1 deletion ext/curl/curl_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions ext/curl/curl_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ typedef struct {

typedef struct {
zend_fcall_info_cache server_push;
zend_fcall_info_cache socket_function;
zend_fcall_info_cache timer_function;
} php_curlm_handlers;

typedef struct {
Expand Down
162 changes: 162 additions & 0 deletions ext/curl/multi.c
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,109 @@ static int _php_server_push_callback(CURL *parent_ch, CURL *easy, size_t num_hea
}
/* }}} */

/* {{{ */
PHP_FUNCTION(curl_multi_socket_action)
{
zval *z_mh;
zval *z_socket;
zend_long ev_bitmask;
zval *z_still_running;

ZEND_PARSE_PARAMETERS_START(4,4)
Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce)
Z_PARAM_ZVAL(z_socket)
Z_PARAM_LONG(ev_bitmask)
Z_PARAM_ZVAL(z_still_running)
ZEND_PARSE_PARAMETERS_END();

php_curlm *mh = Z_CURL_MULTI_P(z_mh);

curl_socket_t socket;

if (Z_TYPE_P(z_socket) == IS_LONG) {
socket = (curl_socket_t) Z_LVAL_P(z_socket);
} else {
php_stream *p_stream;
php_stream_from_zval(p_stream, z_socket);
if (php_stream_cast(
p_stream, PHP_STREAM_AS_FD,
(void**) &socket, REPORT_ERRORS) != SUCCESS) {
return;
}
}

int still_running = zval_get_long(z_still_running);
CURLMcode error = curl_multi_socket_action(
mh->multi, socket, ev_bitmask, &still_running);
ZEND_TRY_ASSIGN_REF_LONG(z_still_running, still_running);

SAVE_CURLM_ERROR(mh, error);
RETURN_LONG((zend_long) error);
}

static int _php_curl_multi_timerfunction(CURLM *multi, zend_long timeout, void *userp) {
php_curlm *mh = (php_curlm *) userp;

zval z_object;
ZVAL_OBJ_COPY(&z_object, &mh->std);

zval z_timeout;
ZVAL_LONG(&z_timeout, timeout);

zval call_args[2] = {z_object, z_timeout};
zval retval;
zend_call_known_fcc(&mh->handlers.timer_function, &retval, /* param_count */ 2, call_args, /* named_params */ NULL);

if (!Z_ISUNDEF(retval)) {
zval_ptr_dtor(&retval);
}

zval_ptr_dtor(&z_object);
return SUCCESS;
}

static int _php_curl_multi_socketfunction(CURL *easy, curl_socket_t socket, int what, void *userp, void *stream) {
php_curlm *mh = (php_curlm *) userp;
php_stream *p_stream;

if (stream == NULL && what != CURL_POLL_REMOVE) {
p_stream = (void*) php_stream_fopen_from_fd(socket, "rw", NULL);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be read only.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, it's the best idea to use streams just a pure fd wrapper. In this case I would just use zend_long to contain it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically the only purpose of the fd here is to be able to pass it to the polling mechanism and then call curl_multi_socket_action when ready so it should not be used for reading / writing by stream. If users want to use stream_select for polling then they should be able to do conversion using php://fd/$socketfd. That said I plan to look to an easier way how use pure fd in stream_select and the new polling API so this should get even easier in the future.

if (!p_stream) {
return FAILURE;
}

if (curl_multi_assign(mh->multi, socket, p_stream) != CURLM_OK) {
php_stream_close(p_stream);
return FAILURE;
}
} else {
p_stream = (php_stream*) stream;
}

zval z_stream;
php_stream_to_zval(p_stream, &z_stream);

zval *z_easy = _php_curl_multi_find_easy_handle(mh, easy);

zval z_what;
ZVAL_LONG(&z_what, what);
zval call_args[3] = {*z_easy, z_stream, z_what};
zval retval;
zend_call_known_fcc(&mh->handlers.socket_function, &retval, /* param_count */ 3, call_args, /* named_params */ NULL);

if (!Z_ISUNDEF(retval)) {
zval_ptr_dtor(&retval);
}

if (what == CURL_POLL_REMOVE && p_stream != NULL) {
curl_multi_assign(mh->multi, socket, NULL);
php_stream_close(p_stream);
}

return SUCCESS;
}
/* }}} */

static bool _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, zval *return_value) /* {{{ */
{
CURLMcode error = CURLM_OK;
Expand Down Expand Up @@ -499,6 +602,49 @@ static bool _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue
error = curl_multi_setopt(mh->multi, CURLMOPT_PUSHDATA, mh);
break;
}
case CURLMOPT_SOCKETFUNCTION: {
if (ZEND_FCC_INITIALIZED(mh->handlers.socket_function)) {
zend_fcc_dtor(&mh->handlers.socket_function);
}

char *error_str = NULL;
if (UNEXPECTED(!zend_is_callable_ex(zvalue, /* object */ NULL, /* check_flags */ 0, /* callable_name */ NULL, &mh->handlers.socket_function, /* error */ &error_str))) {
if (!EG(exception)) {
zend_argument_type_error(2, "must be a valid callback for option CURLMOPT_SOCKETFUNCTION, %s", error_str);
}
efree(error_str);
return false;
}
zend_fcc_addref(&mh->handlers.socket_function);

error = curl_multi_setopt(mh->multi, CURLMOPT_SOCKETFUNCTION, _php_curl_multi_socketfunction);
if (error != CURLM_OK) {
return false;
}
error = curl_multi_setopt(mh->multi, CURLMOPT_SOCKETDATA, mh);
break;
}
case CURLMOPT_TIMERFUNCTION: {
if (ZEND_FCC_INITIALIZED(mh->handlers.timer_function)) {
zend_fcc_dtor(&mh->handlers.timer_function);
}

char *error_str = NULL;
if (UNEXPECTED(!zend_is_callable_ex(zvalue, /* object */ NULL, /* check_flags */ 0, /* callable_name */ NULL, &mh->handlers.timer_function, /* error */ &error_str))) {
if (!EG(exception)) {
zend_argument_type_error(2, "must be a valid callback for option CURLMOPT_TIMERFUNCTION, %s", error_str);
}
efree(error_str);
return false;
}
zend_fcc_addref(&mh->handlers.timer_function);
error = curl_multi_setopt(mh->multi, CURLMOPT_TIMERFUNCTION, _php_curl_multi_timerfunction);
if (error != CURLM_OK) {
return false;
}
error = curl_multi_setopt(mh->multi, CURLMOPT_TIMERDATA, mh);
break;
}
default:
zend_argument_value_error(2, "is not a valid cURL multi option");
error = CURLM_UNKNOWN_OPTION;
Expand Down Expand Up @@ -575,6 +721,14 @@ static void curl_multi_free_obj(zend_object *object)
zend_fcc_dtor(&mh->handlers.server_push);
}

if (ZEND_FCC_INITIALIZED(mh->handlers.timer_function)) {
zend_fcc_dtor(&mh->handlers.timer_function);
}

if (ZEND_FCC_INITIALIZED(mh->handlers.socket_function)) {
zend_fcc_dtor(&mh->handlers.socket_function);
}

zend_object_std_dtor(&mh->std);
}

Expand All @@ -588,6 +742,14 @@ static HashTable *curl_multi_get_gc(zend_object *object, zval **table, int *n)
zend_get_gc_buffer_add_fcc(gc_buffer, &curl_multi->handlers.server_push);
}

if (ZEND_FCC_INITIALIZED(curl_multi->handlers.timer_function)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &curl_multi->handlers.timer_function);
}

if (ZEND_FCC_INITIALIZED(curl_multi->handlers.socket_function)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &curl_multi->handlers.socket_function);
}

zend_llist_position pos;
for (zval *pz_ch = (zval *) zend_llist_get_first_ex(&curl_multi->easyh, &pos); pz_ch;
pz_ch = (zval *) zend_llist_get_next_ex(&curl_multi->easyh, &pos)) {
Expand Down