Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
1 change: 1 addition & 0 deletions Zend/Optimizer/zend_func_infos.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ static const func_info_t func_infos[] = {
F1("curl_multi_strerror", MAY_BE_STRING|MAY_BE_NULL),
F1("curl_share_init", MAY_BE_OBJECT),
F1("curl_share_strerror", MAY_BE_STRING|MAY_BE_NULL),
F1("curl_share_init_persistent", MAY_BE_OBJECT),
F1("curl_strerror", MAY_BE_STRING|MAY_BE_NULL),
F1("curl_version", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_FALSE),
F1("date", MAY_BE_STRING),
Expand Down
12 changes: 12 additions & 0 deletions ext/curl/curl.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -3668,6 +3668,15 @@ final class CurlShareHandle
{
}

/**
* @strict-properties
* @not-serializable
*/
final class CurlSharePersistentHandle
{
public readonly array $options;
}

function curl_close(CurlHandle $handle): void {}

/** @refcount 1 */
Expand Down Expand Up @@ -3750,6 +3759,9 @@ function curl_share_setopt(CurlShareHandle $share_handle, int $option, mixed $va
/** @refcount 1 */
function curl_share_strerror(int $error_code): ?string {}

/** @refcount 1 */
function curl_share_init_persistent(array $share_options): CurlSharePersistentHandle {}

/** @refcount 1 */
function curl_strerror(int $error_code): ?string {}

Expand Down
24 changes: 23 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.

13 changes: 13 additions & 0 deletions ext/curl/curl_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,20 @@
#define SAVE_CURL_ERROR(__handle, __err) \
do { (__handle)->err.no = (int) __err; } while (0)


ZEND_BEGIN_MODULE_GLOBALS(curl)
HashTable persistent_curlsh;
ZEND_END_MODULE_GLOBALS(curl)

ZEND_EXTERN_MODULE_GLOBALS(curl)

#define CURL_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(curl, v)

PHP_MINIT_FUNCTION(curl);
PHP_MSHUTDOWN_FUNCTION(curl);
PHP_MINFO_FUNCTION(curl);
PHP_GINIT_FUNCTION(curl);
PHP_GSHUTDOWN_FUNCTION(curl);

typedef struct {
zend_fcall_info_cache fcc;
Expand Down Expand Up @@ -153,6 +164,8 @@ static inline php_curlsh *curl_share_from_obj(zend_object *obj) {

void curl_multi_register_handlers(void);
void curl_share_register_handlers(void);
void curl_share_persistent_register_handlers(void);
void curl_share_free_persistent_curlsh(zval *data);
void curlfile_register_class(void);
zend_result curl_cast_object(zend_object *obj, zval *result, int type);

Expand Down
48 changes: 39 additions & 9 deletions ext/curl/interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@

#include "curl_arginfo.h"

ZEND_DECLARE_MODULE_GLOBALS(curl)

#ifdef PHP_CURL_NEED_OPENSSL_TSL /* {{{ */
static MUTEX_T *php_curl_openssl_tsl = NULL;

Expand Down Expand Up @@ -215,18 +217,34 @@ zend_module_entry curl_module_entry = {
NULL,
PHP_MINFO(curl),
PHP_CURL_VERSION,
STANDARD_MODULE_PROPERTIES
PHP_MODULE_GLOBALS(curl),
PHP_GINIT(curl),
PHP_GSHUTDOWN(curl),
NULL,
STANDARD_MODULE_PROPERTIES_EX
};
/* }}} */

#ifdef COMPILE_DL_CURL
ZEND_GET_MODULE (curl)
#endif

PHP_GINIT_FUNCTION(curl)
{
zend_hash_init(&curl_globals->persistent_curlsh, 0, NULL, curl_share_free_persistent_curlsh, true);
GC_MAKE_PERSISTENT_LOCAL(&curl_globals->persistent_curlsh);
}

PHP_GSHUTDOWN_FUNCTION(curl)
{
zend_hash_destroy(&curl_globals->persistent_curlsh);
}

/* CurlHandle class */

zend_class_entry *curl_ce;
zend_class_entry *curl_share_ce;
zend_class_entry *curl_share_persistent_ce;
static zend_object_handlers curl_object_handlers;

static zend_object *curl_create_object(zend_class_entry *class_type);
Expand Down Expand Up @@ -410,6 +428,10 @@ PHP_MINIT_FUNCTION(curl)

curl_share_ce = register_class_CurlShareHandle();
curl_share_register_handlers();

curl_share_persistent_ce = register_class_CurlSharePersistentHandle();
curl_share_persistent_register_handlers();

curlfile_register_class();

return SUCCESS;
Expand Down Expand Up @@ -2276,16 +2298,24 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue

case CURLOPT_SHARE:
{
if (Z_TYPE_P(zvalue) == IS_OBJECT && Z_OBJCE_P(zvalue) == curl_share_ce) {
php_curlsh *sh = Z_CURL_SHARE_P(zvalue);
curl_easy_setopt(ch->cp, CURLOPT_SHARE, sh->share);
if (Z_TYPE_P(zvalue) != IS_OBJECT) {
break;
}

if (ch->share) {
OBJ_RELEASE(&ch->share->std);
}
GC_ADDREF(&sh->std);
ch->share = sh;
if (Z_OBJCE_P(zvalue) != curl_share_ce && Z_OBJCE_P(zvalue) != curl_share_persistent_ce) {
break;
}

php_curlsh *sh = Z_CURL_SHARE_P(zvalue);

curl_easy_setopt(ch->cp, CURLOPT_SHARE, sh->share);

if (ch->share) {
OBJ_RELEASE(&ch->share->std);
}

GC_ADDREF(&sh->std);
ch->share = sh;
}
break;

Expand Down
1 change: 1 addition & 0 deletions ext/curl/php_curl.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ extern zend_module_entry curl_module_entry;

PHP_CURL_API extern zend_class_entry *curl_ce;
PHP_CURL_API extern zend_class_entry *curl_share_ce;
PHP_CURL_API extern zend_class_entry *curl_share_persistent_ce;
PHP_CURL_API extern zend_class_entry *curl_multi_ce;
PHP_CURL_API extern zend_class_entry *curl_CURLFile_class;
PHP_CURL_API extern zend_class_entry *curl_CURLStringFile_class;
Expand Down
147 changes: 147 additions & 0 deletions ext/curl/share.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#endif

#include "php.h"
#include "ext/standard/php_array.h"
#include "Zend/zend_exceptions.h"

#include "curl_private.h"

Expand Down Expand Up @@ -134,6 +136,131 @@ PHP_FUNCTION(curl_share_strerror)
}
/* }}} */

/**
* Initialize a persistent curl share handle with the given share options, reusing an existing one if found.
*
* Throws an exception if the share options are invalid.
*/
PHP_FUNCTION(curl_share_init_persistent)
{
zval *share_opts = NULL, *entry = NULL;
zend_ulong persistent_id = 0;

php_curlsh *sh;

CURLSHcode error;

ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY_EX(share_opts, 0, 1)
ZEND_PARSE_PARAMETERS_END();

object_init_ex(return_value, curl_share_persistent_ce);
sh = Z_CURL_SHARE_P(return_value);

ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(share_opts), entry) {
ZVAL_DEREF(entry);

bool failed = false;
zend_ulong option = zval_try_get_long(entry, &failed);

if (failed) {
zend_argument_type_error(1, "must contain only longs, %s given", zend_zval_value_name(entry));
goto error;
}

switch (option) {
// Disallowed options
case CURL_LOCK_DATA_COOKIE:
zend_argument_value_error(1, "CURL_LOCK_DATA_COOKIE is not allowed");
goto error;

// Allowed options
case CURL_LOCK_DATA_DNS:
persistent_id |= 1 << 0;
break;
case CURL_LOCK_DATA_SSL_SESSION:
persistent_id |= 1 << 1;
break;
case CURL_LOCK_DATA_CONNECT:
persistent_id |= 1 << 2;
break;
case CURL_LOCK_DATA_PSL:
persistent_id |= 1 << 3;
break;

// Unknown options
default:
zend_argument_value_error(1, "must contain only CURL_LOCK_DATA_* constants");
goto error;
}
} ZEND_HASH_FOREACH_END();

zend_array_sort(Z_ARRVAL_P(share_opts), php_array_data_compare_unstable_i, 1);
zend_update_property(curl_share_persistent_ce, Z_OBJ_P(return_value), "options", sizeof("options") - 1, share_opts);

if (persistent_id) {
zval *persisted = zend_hash_index_find(&CURL_G(persistent_curlsh), persistent_id);

if (persisted) {
sh->share = Z_PTR_P(persisted);

return;
}
}

// We could not find an existing share handle, so we'll have to create one.
sh->share = curl_share_init();

// Apply $share_options to the handle.
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(share_opts), entry) {
ZVAL_DEREF(entry);

error = curl_share_setopt(sh->share, CURLSHOPT_SHARE, zval_get_long(entry));

if (error != CURLSHE_OK) {
zend_throw_exception_ex(
NULL,
0,
"Could not construct persistent cURL share handle: %s",
curl_share_strerror(error)
);

goto error;
}
} ZEND_HASH_FOREACH_END();

zend_hash_index_add_new_ptr(
&CURL_G(persistent_curlsh),
persistent_id,
sh->share
);

return;

error:
if (sh->share) {
curl_share_cleanup(sh->share);
}

RETURN_THROWS();
}

/**
* Free a persistent curl share handle from the module global HashTable.
*
* See PHP_GINIT_FUNCTION in ext/curl/interface.c.
*/
void curl_share_free_persistent_curlsh(zval *data)
{
CURLSH *handle = Z_PTR_P(data);

if (!handle) {
return;
}

curl_share_cleanup(handle);
}

/* CurlShareHandle class */

static zend_object *curl_share_create_object(zend_class_entry *class_type) {
Expand Down Expand Up @@ -171,3 +298,23 @@ void curl_share_register_handlers(void) {
curl_share_handlers.clone_obj = NULL;
curl_share_handlers.compare = zend_objects_not_comparable;
}

/* CurlSharePersistentHandle class */

static zend_object_handlers curl_share_persistent_handlers;

static zend_function *curl_share_persistent_get_constructor(zend_object *object) {
zend_throw_error(NULL, "Cannot directly construct CurlSharePersistentHandle, use curl_share_init_persistent() instead");
return NULL;
}

void curl_share_persistent_register_handlers(void) {
curl_share_persistent_ce->create_object = curl_share_create_object;
curl_share_persistent_ce->default_object_handlers = &curl_share_persistent_handlers;

memcpy(&curl_share_persistent_handlers, &std_object_handlers, sizeof(zend_object_handlers));
curl_share_persistent_handlers.offset = XtOffsetOf(php_curlsh, std);
curl_share_persistent_handlers.get_constructor = curl_share_persistent_get_constructor;
curl_share_persistent_handlers.clone_obj = NULL;
curl_share_persistent_handlers.compare = zend_objects_not_comparable;
}
Loading
Loading