Skip to content

Commit 6472289

Browse files
committed
curl: Make CurlHandle::$share a real property
1 parent 1d4b53a commit 6472289

File tree

6 files changed

+99
-26
lines changed

6 files changed

+99
-26
lines changed

ext/curl/curl.stub.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3713,6 +3713,7 @@
37133713
*/
37143714
final class CurlHandle
37153715
{
3716+
private null|CurlShareHandle|CurlSharePersistentHandle $share = null;
37163717
}
37173718

37183719
/**

ext/curl/curl_arginfo.h

Lines changed: 14 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/curl/curl_private.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,6 @@ typedef struct {
114114
zval postfields;
115115
/* For CURLOPT_PRIVATE */
116116
zval private_data;
117-
/* CurlShareHandle object set using CURLOPT_SHARE. */
118-
struct _php_curlsh *share;
119117
zend_object std;
120118
} php_curl;
121119

ext/curl/interface.c

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ static zend_object *curl_create_object(zend_class_entry *class_type);
217217
static void curl_free_obj(zend_object *object);
218218
static HashTable *curl_get_gc(zend_object *object, zval **table, int *n);
219219
static zend_function *curl_get_constructor(zend_object *object);
220+
static zval *curl_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot);
220221
static zend_object *curl_clone_obj(zend_object *object);
221222
php_curl *init_curl_handle_into_zval(zval *curl);
222223
static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpostfields);
@@ -370,6 +371,7 @@ PHP_MINIT_FUNCTION(curl)
370371
curl_object_handlers.clone_obj = curl_clone_obj;
371372
curl_object_handlers.cast_object = curl_cast_object;
372373
curl_object_handlers.compare = zend_objects_not_comparable;
374+
curl_object_handlers.write_property = curl_write_property;
373375

374376
curl_multi_ce = register_class_CurlMultiHandle();
375377
curl_multi_register_handlers();
@@ -402,6 +404,48 @@ static zend_function *curl_get_constructor(zend_object *object) {
402404
return NULL;
403405
}
404406

407+
static zval *curl_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot)
408+
{
409+
if (zend_string_equals_literal(name, "share")) {
410+
zval old_value;
411+
/* Backup the old value, because the reassignment in zend_std_write_property might
412+
* cause a previously attached share handle to become RC=0 and we need to detach
413+
* it first before we may destruct the PHP object. */
414+
ZVAL_COPY(&old_value, zend_std_read_property(object, name, BP_VAR_R, cache_slot, NULL));
415+
416+
/* Call zend_std_write_property first to rely on its validation features (e.g. for types)
417+
* before actually attaching the handle in the curl API. */
418+
zval *result = zend_std_write_property(object, name, value, cache_slot);
419+
420+
CURLSH *sh;
421+
switch (Z_TYPE_P(result)) {
422+
case IS_NULL:
423+
sh = NULL;
424+
break;
425+
case IS_OBJECT:
426+
ZEND_ASSERT(Z_OBJCE_P(result) == curl_share_ce || Z_OBJCE_P(result) == curl_share_persistent_ce);
427+
sh = Z_CURL_SHARE_P(result)->share;
428+
break;
429+
EMPTY_SWITCH_DEFAULT_CASE();
430+
}
431+
432+
if (curl_easy_setopt(curl_from_obj(object)->cp, CURLOPT_SHARE, sh) != CURLE_OK) {
433+
/* If the new handle is rejected by curl, we restore the old PHP property. */
434+
zend_std_write_property(object, name, &old_value, cache_slot);
435+
zend_throw_error(NULL, "Failed to set share handle");
436+
return &EG(error_zval);
437+
}
438+
439+
/* The old value is no longer attached to the underlying curl structures and
440+
* can safely be destructed. */
441+
zval_ptr_dtor(&old_value);
442+
443+
return result;
444+
} else {
445+
return zend_std_write_property(object, name, value, cache_slot);
446+
}
447+
}
448+
405449
static zend_object *curl_clone_obj(zend_object *object) {
406450
php_curl *ch;
407451
CURL *cp;
@@ -2239,24 +2283,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
22392283

22402284
case CURLOPT_SHARE:
22412285
{
2242-
if (Z_TYPE_P(zvalue) != IS_OBJECT) {
2243-
break;
2244-
}
2245-
2246-
if (Z_OBJCE_P(zvalue) != curl_share_ce && Z_OBJCE_P(zvalue) != curl_share_persistent_ce) {
2247-
break;
2248-
}
2249-
2250-
php_curlsh *sh = Z_CURL_SHARE_P(zvalue);
2251-
2252-
curl_easy_setopt(ch->cp, CURLOPT_SHARE, sh->share);
2253-
2254-
if (ch->share) {
2255-
OBJ_RELEASE(&ch->share->std);
2256-
}
2257-
2258-
GC_ADDREF(&sh->std);
2259-
ch->share = sh;
2286+
zend_update_property(ch->std.ce, &ch->std, "share", strlen("share"), zvalue);
22602287
}
22612288
break;
22622289

@@ -2858,10 +2885,6 @@ static void curl_free_obj(zend_object *object)
28582885
zval_ptr_dtor(&ch->postfields);
28592886
zval_ptr_dtor(&ch->private_data);
28602887

2861-
if (ch->share) {
2862-
OBJ_RELEASE(&ch->share->std);
2863-
}
2864-
28652888
zend_object_std_dtor(&ch->std);
28662889
}
28672890
/* }}} */

ext/curl/share.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,8 @@ void curl_share_free_obj(zend_object *object)
294294
{
295295
php_curlsh *sh = curl_share_from_obj(object);
296296

297-
curl_share_cleanup(sh->share);
297+
CURLSHcode result = curl_share_cleanup(sh->share);
298+
ZEND_ASSERT(result == CURLSHE_OK);
298299
zend_object_std_dtor(&sh->std);
299300
}
300301

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
Attached share handles are not preserved when cloning
3+
--EXTENSIONS--
4+
curl
5+
--FILE--
6+
<?php
7+
8+
$sh = curl_share_init();
9+
10+
$ch1 = curl_init();
11+
curl_setopt($ch1, CURLOPT_URL, 'file://' . __DIR__ . '/curl_testdata1.txt');
12+
curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);
13+
curl_setopt($ch1, CURLOPT_SHARE, $sh);
14+
15+
$ch2 = clone $ch1;
16+
$ch3 = curl_copy_handle($ch2);
17+
18+
// Make sure nothing bad handles if the share handle is unset early.
19+
unset($sh);
20+
21+
var_dump($ch1, $ch2, $ch3);
22+
23+
?>
24+
--EXPECTF--
25+
object(CurlHandle)#%d (1) {
26+
["share":"CurlHandle":private]=>
27+
object(CurlShareHandle)#%d (0) {
28+
}
29+
}
30+
object(CurlHandle)#%d (1) {
31+
["share":"CurlHandle":private]=>
32+
NULL
33+
}
34+
object(CurlHandle)#%d (1) {
35+
["share":"CurlHandle":private]=>
36+
NULL
37+
}

0 commit comments

Comments
 (0)