]> granicus.if.org Git - php/commitdiff
Convert CURL resources to objects
authorMáté Kocsis <kocsismate@woohoolabs.com>
Wed, 17 Jun 2020 14:05:55 +0000 (16:05 +0200)
committerMáté Kocsis <kocsismate@woohoolabs.com>
Wed, 17 Jun 2020 14:11:57 +0000 (16:11 +0200)
Closes GH-5402

Co-authored-by: Nikita Popov <nikita.ppv@gmail.com>
20 files changed:
UPGRADING
ext/curl/curl.stub.php
ext/curl/curl_arginfo.h
ext/curl/interface.c
ext/curl/multi.c
ext/curl/php_curl.h
ext/curl/share.c
ext/curl/tests/bug48514.phpt
ext/curl/tests/bug72202.phpt
ext/curl/tests/bug77535.phpt
ext/curl/tests/curl_basic_014.phpt
ext/curl/tests/curl_close_basic.phpt
ext/curl/tests/curl_handle_clone.phpt [new file with mode: 0644]
ext/curl/tests/curl_multi_close_basic.phpt
ext/curl/tests/curl_multi_close_basic001.phpt
ext/curl/tests/curl_multi_info_read.phpt
ext/curl/tests/curl_multi_init_basic.phpt
ext/curl/tests/curl_share_close_basic001.phpt
ext/opcache/Optimizer/zend_func_info.c
ext/posix/tests/posix_ttyname_error_wrongparams.phpt

index 7e8b5971bac8d55366a7d560b21397451600ba84..794b91f9432f09b09e01ee972eca0dc929d47396 100644 (file)
--- a/UPGRADING
+++ b/UPGRADING
@@ -739,6 +739,21 @@ PHP 8.0 UPGRADE NOTES
 
 - CURL:
   . The CURL extension now requires at least libcurl 7.29.0.
+  . curl_init() will now return a CurlHandle object rather than a resource.
+    Return value checks using is_resource() should be replaced with
+    checks for `false`. The curl_close() function no longer has an effect,
+    instead the CurlHandle instance is automatically destroyed if it is no
+    longer referenced.
+  . curl_multi_init() will now return a CurlMultiHandle object rather than a
+    resource. Return value checks using is_resource() should be replaced with
+    checks for `false`. The curl_multi_close() function no longer has an effect,
+    instead the CurlMultiHandle instance is automatically destroyed if it is no
+    longer referenced.
+  . curl_share_init() will now return a CurlShareHandle object rather than a
+    resource. Return value checks using is_resource() should be replaced with
+    checks for `false`. The curl_share_close() function no longer has an effect,
+    instead the CurlShareHandle instance is automatically destroyed if it is no
+    longer referenced.
 
 - Enchant:
   . The enchant extension now uses libenchant-2 by default when available.
index ac998c45650a1716eb2b06f439453cac8564b218..f7e20e30f6e2c4b0b808071096d2460e7f436b3a 100644 (file)
 
 /** @generate-function-entries */
 
-/** @param resource $handle */
-function curl_close($handle): void {}
+final class CurlHandle
+{
+}
 
-/**
- * @param resource $handle
- * @return resource|false
- */
-function curl_copy_handle($handle) {}
+final class CurlMultiHandle
+{
+}
 
-/** @param resource $handle */
-function curl_errno($handle): int {}
+final class CurlShareHandle
+{
+}
 
-/** @param resource $handle */
-function curl_error($handle): string {}
+function curl_close(CurlHandle $handle): void {}
+
+function curl_copy_handle(CurlHandle $handle): CurlHandle|false {}
+
+function curl_errno(CurlHandle $handle): int {}
+
+function curl_error(CurlHandle $handle): string {}
 
 #if LIBCURL_VERSION_NUM >= 0x070f04 /* 7.15.4 */
-/** @param resource $handle */
-function curl_escape($handle, string $string): string|false {}
+function curl_escape(CurlHandle $handle, string $string): string|false {}
 
-/** @param resource $handle */
-function curl_unescape($handle, string $string): string|false {}
+function curl_unescape(CurlHandle $handle, string $string): string|false {}
 
-/**
- * @param resource $multi_handle
- * @param mixed $value
- */
-function curl_multi_setopt($multi_handle, int $option, $value): bool {}
+function curl_multi_setopt(CurlMultiHandle $multi_handle, int $option, mixed $value): bool {}
 
 #endif
 
-/** @param resource $handle */
-function curl_exec($handle): string|bool {}
+function curl_exec(CurlHandle $handle): string|bool {}
 
-function curl_file_create(
-    string $filename,
-    string $mimetype = UNKNOWN,
-    string $postname = UNKNOWN
-): CURLFile {}
+function curl_file_create(string $filename, string $mimetype = UNKNOWN, string $postname = UNKNOWN): CURLFile {}
 
-/**
- * @param resource $handle
- * @return mixed
- */
-function curl_getinfo($handle, int $option = UNKNOWN) {}
+function curl_getinfo(CurlHandle $handle, int $option = UNKNOWN): mixed {}
 
-/**
- * @param resource $handle
- * @return resource|false
- */
-function curl_init(string $url = UNKNOWN) {}
+function curl_init(string $url = UNKNOWN): CurlHandle|false {}
 
-/**
- * @param resource $multi_handle
- * @param resource $handle
- */
-function curl_multi_add_handle($multi_handle, $handle): int {}
+function curl_multi_add_handle(CurlMultiHandle $multi_handle, CurlHandle $handle): int {}
 
-/** @param resource $multi_handle */
-function curl_multi_close($multi_handle): void {}
+function curl_multi_close(CurlMultiHandle $multi_handle): void {}
 
-/** @param resource $multi_handle */
-function curl_multi_errno($multi_handle): int {}
+function curl_multi_errno(CurlMultiHandle $multi_handle): int {}
 
-/** @param resource $multi_handle */
-function curl_multi_exec($multi_handle, &$still_running): int {}
+/** @param int $still_running */
+function curl_multi_exec(CurlMultiHandle $multi_handle, &$still_running): int {}
 
-/** @param resource $multi_handle */
-function curl_multi_getcontent($multi_handle): ?string {}
+function curl_multi_getcontent(CurlHandle $multi_handle): ?string {}
 
-/** @param resource $multi_handle */
-function curl_multi_info_read($multi_handle, &$msgs_in_queue = null): array|false {}
+/** @param int|null $msgs_in_queue */
+function curl_multi_info_read(CurlMultiHandle $multi_handle, &$msgs_in_queue = null): array|false {}
 
-/** @return resource */
-function curl_multi_init() {}
+function curl_multi_init(): CurlMultiHandle {}
 
-/**
- * @param resource $multi_handle
- * @param resource $handle
- */
-function curl_multi_remove_handle($multi_handle, $handle): int {}
+function curl_multi_remove_handle(CurlMultiHandle $multi_handle, CurlHandle $handle): int {}
 
-/** @param resource $multi_handle */
-function curl_multi_select($multi_handle, float $timeout = 1.0): int {}
+function curl_multi_select(CurlMultiHandle $multi_handle, float $timeout = 1.0): int {}
 
 function curl_multi_strerror(int $error_number): ?string {}
 
 #if LIBCURL_VERSION_NUM >= 0x071200 /* 7.18.0 */
-/** @param resource $handle */
-function curl_pause($handle, int $bitmask): int {}
+function curl_pause(CurlHandle $handle, int $bitmask): int {}
 #endif
 
-/** @param resource $handle */
-function curl_reset($handle): void {}
+function curl_reset(CurlHandle $handle): void {}
 
-/** @param resource $handle */
-function curl_setopt_array($handle, array $options): bool {}
+function curl_setopt_array(CurlHandle $handle, array $options): bool {}
 
-/**
- * @param resource $handle
- * @param mixed $value
- */
-function curl_setopt($handle, int $option, $value): bool {}
+function curl_setopt(CurlHandle $handle, int $option, mixed $value): bool {}
 
-/** @param resource $share_handle */
-function curl_share_close($share_handle): void {}
+function curl_share_close(CurlShareHandle $share_handle): void {}
 
-/** @param resource $share_handle */
-function curl_share_errno($share_handle): int {}
+function curl_share_errno(CurlShareHandle $share_handle): int {}
 
-/** @return resource */
-function curl_share_init() {}
+function curl_share_init(): CurlShareHandle {}
 
-/** @param resource $share_handle */
-function curl_share_setopt($share_handle, int $option, $value): bool {}
+function curl_share_setopt(CurlShareHandle $share_handle, int $option, mixed $value): bool {}
 
 function curl_share_strerror(int $error_number): ?string {}
 
index efc57e9148733bfe05c48a30cec60270b3d71c62..72f68a63079bc520d30d1f67c20e9c2a88a1cc93 100644 (file)
@@ -1,24 +1,24 @@
 /* This is a generated file, edit the .stub.php file instead. */
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_close, 0, 1, IS_VOID, 0)
-       ZEND_ARG_INFO(0, handle)
+       ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_curl_copy_handle, 0, 0, 1)
-       ZEND_ARG_INFO(0, handle)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_curl_copy_handle, 0, 1, CurlHandle, MAY_BE_FALSE)
+       ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_errno, 0, 1, IS_LONG, 0)
-       ZEND_ARG_INFO(0, handle)
+       ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_error, 0, 1, IS_STRING, 0)
-       ZEND_ARG_INFO(0, handle)
+       ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)
 ZEND_END_ARG_INFO()
 
 #if LIBCURL_VERSION_NUM >= 0x070f04 /* 7.15.4 */
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_curl_escape, 0, 2, MAY_BE_STRING|MAY_BE_FALSE)
-       ZEND_ARG_INFO(0, handle)
+       ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)
        ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 #endif
@@ -29,14 +29,14 @@ ZEND_END_ARG_INFO()
 
 #if LIBCURL_VERSION_NUM >= 0x070f04 /* 7.15.4 */
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_setopt, 0, 3, _IS_BOOL, 0)
-       ZEND_ARG_INFO(0, multi_handle)
+       ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)
        ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0)
-       ZEND_ARG_INFO(0, value)
+       ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 #endif
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_curl_exec, 0, 1, MAY_BE_STRING|MAY_BE_BOOL)
-       ZEND_ARG_INFO(0, handle)
+       ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_curl_file_create, 0, 1, CURLFile, 0)
@@ -45,49 +45,49 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_curl_file_create, 0, 1, CURLFile,
        ZEND_ARG_TYPE_INFO(0, postname, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_curl_getinfo, 0, 0, 1)
-       ZEND_ARG_INFO(0, handle)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_getinfo, 0, 1, IS_MIXED, 0)
+       ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)
        ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_curl_init, 0, 0, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_curl_init, 0, 0, CurlHandle, MAY_BE_FALSE)
        ZEND_ARG_TYPE_INFO(0, url, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_add_handle, 0, 2, IS_LONG, 0)
-       ZEND_ARG_INFO(0, multi_handle)
-       ZEND_ARG_INFO(0, handle)
+       ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)
+       ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_close, 0, 1, IS_VOID, 0)
-       ZEND_ARG_INFO(0, multi_handle)
+       ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_errno, 0, 1, IS_LONG, 0)
-       ZEND_ARG_INFO(0, multi_handle)
+       ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_exec, 0, 2, IS_LONG, 0)
-       ZEND_ARG_INFO(0, multi_handle)
+       ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)
        ZEND_ARG_INFO(1, still_running)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_getcontent, 0, 1, IS_STRING, 1)
-       ZEND_ARG_INFO(0, multi_handle)
+       ZEND_ARG_OBJ_INFO(0, multi_handle, CurlHandle, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_curl_multi_info_read, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE)
-       ZEND_ARG_INFO(0, multi_handle)
+       ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)
        ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, msgs_in_queue, "null")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_curl_multi_init, 0, 0, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_curl_multi_init, 0, 0, CurlMultiHandle, 0)
 ZEND_END_ARG_INFO()
 
 #define arginfo_curl_multi_remove_handle arginfo_curl_multi_add_handle
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_select, 0, 1, IS_LONG, 0)
-       ZEND_ARG_INFO(0, multi_handle)
+       ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)
        ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "1.0")
 ZEND_END_ARG_INFO()
 
@@ -97,7 +97,7 @@ ZEND_END_ARG_INFO()
 
 #if LIBCURL_VERSION_NUM >= 0x071200 /* 7.18.0 */
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_pause, 0, 2, IS_LONG, 0)
-       ZEND_ARG_INFO(0, handle)
+       ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)
        ZEND_ARG_TYPE_INFO(0, bitmask, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 #endif
@@ -105,30 +105,31 @@ ZEND_END_ARG_INFO()
 #define arginfo_curl_reset arginfo_curl_close
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_setopt_array, 0, 2, _IS_BOOL, 0)
-       ZEND_ARG_INFO(0, handle)
+       ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)
        ZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_setopt, 0, 3, _IS_BOOL, 0)
-       ZEND_ARG_INFO(0, handle)
+       ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)
        ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0)
-       ZEND_ARG_INFO(0, value)
+       ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_share_close, 0, 1, IS_VOID, 0)
-       ZEND_ARG_INFO(0, share_handle)
+       ZEND_ARG_OBJ_INFO(0, share_handle, CurlShareHandle, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_share_errno, 0, 1, IS_LONG, 0)
-       ZEND_ARG_INFO(0, share_handle)
+       ZEND_ARG_OBJ_INFO(0, share_handle, CurlShareHandle, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_curl_share_init arginfo_curl_multi_init
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_curl_share_init, 0, 0, CurlShareHandle, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_share_setopt, 0, 3, _IS_BOOL, 0)
-       ZEND_ARG_INFO(0, share_handle)
+       ZEND_ARG_OBJ_INFO(0, share_handle, CurlShareHandle, 0)
        ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0)
-       ZEND_ARG_INFO(0, value)
+       ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
 #define arginfo_curl_share_strerror arginfo_curl_multi_strerror
@@ -225,3 +226,18 @@ static const zend_function_entry ext_functions[] = {
        ZEND_FE(curl_version, arginfo_curl_version)
        ZEND_FE_END
 };
+
+
+static const zend_function_entry class_CurlHandle_methods[] = {
+       ZEND_FE_END
+};
+
+
+static const zend_function_entry class_CurlMultiHandle_methods[] = {
+       ZEND_FE_END
+};
+
+
+static const zend_function_entry class_CurlShareHandle_methods[] = {
+       ZEND_FE_END
+};
index dc79a9e2d7c63bde6e542c5498be89dc9e73c3d4..bdc284b4abb366259e927a9d6994c09ee0e2931b 100644 (file)
@@ -21,6 +21,8 @@
 #endif
 
 #include "php.h"
+#include "Zend/zend_interfaces.h"
+#include "Zend/zend_exceptions.h"
 
 #ifdef HAVE_CURL
 
 #include "php_curl.h"
 #include "curl_arginfo.h"
 
-int  le_curl;
-int  le_curl_multi_handle;
-int  le_curl_share_handle;
-
 #ifdef PHP_CURL_NEED_OPENSSL_TSL /* {{{ */
 static MUTEX_T *php_curl_openssl_tsl = NULL;
 
@@ -88,9 +86,6 @@ static ZEND_ATTRIBUTE_UNUSED unsigned long php_curl_ssl_id(void)
 #endif
 /* }}} */
 
-static void _php_curl_close_ex(php_curl *ch);
-static void _php_curl_close(zend_resource *rsrc);
-
 #define CAAL(s, v) add_assoc_long_ex(return_value, s, sizeof(s) - 1, (zend_long) v);
 #define CAAD(s, v) add_assoc_double_ex(return_value, s, sizeof(s) - 1, (double) v);
 #define CAAS(s, v) add_assoc_string_ex(return_value, s, sizeof(s) - 1, (char *) (v ? v : ""));
@@ -235,6 +230,20 @@ zend_module_entry curl_module_entry = {
 ZEND_GET_MODULE (curl)
 #endif
 
+/* CurlHandle class */
+
+zend_class_entry *curl_ce;
+zend_class_entry *curl_share_ce;
+static zend_object_handlers curl_object_handlers;
+
+static zend_object *curl_create_object(zend_class_entry *class_type);
+static void curl_free_obj(zend_object *object);
+static HashTable *curl_get_gc(zend_object *object, zval **table, int *n);
+static zend_function *curl_get_constructor(zend_object *object);
+static zend_object *curl_clone_obj(zend_object *object);
+php_curl *init_curl_handle_into_zval(zval *curl);
+static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields);
+
 /* {{{ PHP_INI_BEGIN */
 PHP_INI_BEGIN()
        PHP_INI_ENTRY("curl.cainfo", "", PHP_INI_SYSTEM, NULL)
@@ -365,10 +374,6 @@ PHP_MINFO_FUNCTION(curl)
  */
 PHP_MINIT_FUNCTION(curl)
 {
-       le_curl = zend_register_list_destructors_ex(_php_curl_close, NULL, "curl", module_number);
-       le_curl_multi_handle = zend_register_list_destructors_ex(_php_curl_multi_close, NULL, "curl_multi", module_number);
-       le_curl_share_handle = zend_register_list_destructors_ex(_php_curl_share_close, NULL, "curl_share", module_number);
-
        REGISTER_INI_ENTRIES();
 
        /* See http://curl.haxx.se/lxr/source/docs/libcurl/symbols-in-versions
@@ -1185,12 +1190,119 @@ PHP_MINIT_FUNCTION(curl)
                return FAILURE;
        }
 
+       zend_class_entry ce;
+       INIT_CLASS_ENTRY(ce, "CurlHandle", class_CurlHandle_methods);
+       curl_ce = zend_register_internal_class(&ce);
+       curl_ce->ce_flags |= ZEND_ACC_FINAL;
+       curl_ce->create_object = curl_create_object;
+       curl_ce->serialize = zend_class_serialize_deny;
+       curl_ce->unserialize = zend_class_unserialize_deny;
+
+       memcpy(&curl_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
+       curl_object_handlers.offset = XtOffsetOf(php_curl, std);
+       curl_object_handlers.free_obj = curl_free_obj;
+       curl_object_handlers.get_gc = curl_get_gc;
+       curl_object_handlers.get_constructor = curl_get_constructor;
+       curl_object_handlers.clone_obj = curl_clone_obj;
+
+       curl_multi_register_class(class_CurlMultiHandle_methods);
+       curl_share_register_class(class_CurlShareHandle_methods);
        curlfile_register_class();
 
        return SUCCESS;
 }
 /* }}} */
 
+/* CurlHandle class */
+
+static zend_object *curl_create_object(zend_class_entry *class_type) {
+       php_curl *intern = zend_object_alloc(sizeof(php_curl), class_type);
+
+       zend_object_std_init(&intern->std, class_type);
+       object_properties_init(&intern->std, class_type);
+       intern->std.handlers = &curl_object_handlers;
+
+       return &intern->std;
+}
+
+static zend_function *curl_get_constructor(zend_object *object) {
+       zend_throw_error(NULL, "Cannot directly construct CurlHandle, use curl_init() instead");
+       return NULL;
+}
+
+static zend_object *curl_clone_obj(zend_object *object) {
+       php_curl *ch;
+       CURL *cp;
+       zval *postfields;
+       zend_object *clone_object;
+       php_curl *clone_ch;
+
+       clone_object = curl_create_object(curl_ce);
+       clone_ch = curl_from_obj(clone_object);
+       init_curl_handle(clone_ch);
+
+       ch = curl_from_obj(object);
+       cp = curl_easy_duphandle(ch->cp);
+       if (!cp) {
+               zend_throw_exception(NULL, "Failed to clone CurlHandle", 0);
+               return &clone_ch->std;
+       }
+
+       clone_ch->cp = cp;
+       _php_setup_easy_copy_handlers(clone_ch, ch);
+
+       postfields = &clone_ch->postfields;
+       if (Z_TYPE_P(postfields) != IS_UNDEF) {
+               if (build_mime_structure_from_hash(clone_ch, postfields) != SUCCESS) {
+                       zend_throw_exception(NULL, "Failed to clone CurlHandle", 0);
+                       return &clone_ch->std;
+               }
+       }
+
+       return &clone_ch->std;
+}
+
+static HashTable *curl_get_gc(zend_object *object, zval **table, int *n)
+{
+       php_curl *curl = curl_from_obj(object);
+
+       zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
+
+       zend_get_gc_buffer_add_zval(gc_buffer, &curl->postfields);
+       if (curl->handlers) {
+               if (curl->handlers->read) {
+                       zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers->read->func_name);
+                       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);
+                       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);
+                       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 LIBCURL_VERSION_NUM >= 0x071500
+               if (curl->handlers->fnmatch) {
+                       zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers->fnmatch->func_name);
+               }
+#endif
+
+               zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers->std_err);
+       }
+
+       zend_get_gc_buffer_use(gc_buffer, table, n);
+
+       return zend_std_get_properties(object);
+}
+
 /* {{{ PHP_MSHUTDOWN_FUNCTION
  */
 PHP_MSHUTDOWN_FUNCTION(curl)
@@ -1255,8 +1367,8 @@ static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx)
                        int  error;
                        zend_fcall_info fci;
 
-                       GC_ADDREF(ch->res);
-                       ZVAL_RES(&argv[0], ch->res);
+                       GC_ADDREF(&ch->std);
+                       ZVAL_OBJ(&argv[0], &ch->std);
                        ZVAL_STRINGL(&argv[1], data, length);
 
                        fci.size = sizeof(fci);
@@ -1302,8 +1414,8 @@ static int curl_fnmatch(void *ctx, const char *pattern, const char *string)
                        int  error;
                        zend_fcall_info fci;
 
-                       GC_ADDREF(ch->res);
-                       ZVAL_RES(&argv[0], ch->res);
+                       GC_ADDREF(&ch->std);
+                       ZVAL_OBJ(&argv[0], &ch->std);
                        ZVAL_STRING(&argv[1], pattern);
                        ZVAL_STRING(&argv[2], string);
 
@@ -1354,8 +1466,8 @@ static size_t curl_progress(void *clientp, double dltotal, double dlnow, double
                        int  error;
                        zend_fcall_info fci;
 
-                       GC_ADDREF(ch->res);
-                       ZVAL_RES(&argv[0], ch->res);
+                       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);
@@ -1408,8 +1520,8 @@ static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx)
                        int  error;
                        zend_fcall_info fci;
 
-                       GC_ADDREF(ch->res);
-                       ZVAL_RES(&argv[0], ch->res);
+                       GC_ADDREF(&ch->std);
+                       ZVAL_OBJ(&argv[0], &ch->std);
                        if (t->res) {
                                GC_ADDREF(t->res);
                                ZVAL_RES(&argv[1], t->res);
@@ -1477,8 +1589,8 @@ static size_t curl_write_header(char *data, size_t size, size_t nmemb, void *ctx
                        int  error;
                        zend_fcall_info fci;
 
-                       GC_ADDREF(ch->res);
-                       ZVAL_RES(&argv[0], ch->res);
+                       GC_ADDREF(&ch->std);
+                       ZVAL_OBJ(&argv[0], &ch->std);
                        ZVAL_STRINGL(&argv[1], data, length);
 
                        fci.size = sizeof(fci);
@@ -1643,11 +1755,20 @@ PHP_FUNCTION(curl_version)
 }
 /* }}} */
 
-/* {{{ alloc_curl_handle
- */
-php_curl *alloc_curl_handle()
+php_curl *init_curl_handle_into_zval(zval *curl)
+{
+       php_curl *ch;
+
+       object_init_ex(curl, curl_ce);
+       ch = Z_CURL_P(curl);
+
+       init_curl_handle(ch);
+
+       return ch;
+}
+
+void init_curl_handle(php_curl *ch)
 {
-       php_curl *ch               = ecalloc(1, sizeof(php_curl));
        ch->to_free                = ecalloc(1, sizeof(struct _php_curl_free));
        ch->handlers               = ecalloc(1, sizeof(php_curl_handlers));
        ch->handlers->write        = ecalloc(1, sizeof(php_curl_write));
@@ -1666,11 +1787,9 @@ php_curl *alloc_curl_handle()
 
        ch->to_free->slist = emalloc(sizeof(HashTable));
        zend_hash_init(ch->to_free->slist, 4, NULL, curl_free_slist, 0);
-#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */
        ZVAL_UNDEF(&ch->postfields);
-#endif
-       return ch;
 }
+
 /* }}} */
 
 /* {{{ create_certinfo
@@ -1742,7 +1861,7 @@ static void _php_curl_set_default_options(php_curl *ch)
 }
 /* }}} */
 
-/* {{{ proto resource curl_init([string url])
+/* {{{ proto CurlHandle curl_init([string url])
    Initialize a cURL session */
 PHP_FUNCTION(curl_init)
 {
@@ -1761,7 +1880,7 @@ PHP_FUNCTION(curl_init)
                RETURN_FALSE;
        }
 
-       ch = alloc_curl_handle();
+       ch = init_curl_handle_into_zval(return_value);
 
        ch->cp = cp;
 
@@ -1773,13 +1892,10 @@ PHP_FUNCTION(curl_init)
 
        if (url) {
                if (php_curl_option_url(ch, ZSTR_VAL(url), ZSTR_LEN(url)) == FAILURE) {
-                       _php_curl_close_ex(ch);
+                       zval_ptr_dtor(return_value);
                        RETURN_FALSE;
                }
        }
-
-       ZVAL_RES(return_value, zend_register_resource(ch, le_curl));
-       ch->res = Z_RES_P(return_value);
 }
 /* }}} */
 
@@ -2070,24 +2186,21 @@ static inline int build_mime_structure_from_hash(php_curl *ch, zval *zpostfields
 }
 /* }}} */
 
-/* {{{ proto resource curl_copy_handle(resource ch)
+/* {{{ proto CurlHandle curl_copy_handle(CurlHandle ch)
    Copy a cURL handle along with all of it's preferences */
 PHP_FUNCTION(curl_copy_handle)
 {
+       php_curl        *ch;
        CURL            *cp;
        zval            *zid;
-       php_curl        *ch, *dupch;
-#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */
+       php_curl        *dupch;
        zval            *postfields;
-#endif
 
        ZEND_PARSE_PARAMETERS_START(1,1)
-               Z_PARAM_RESOURCE(zid)
+               Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((ch = (php_curl*)zend_fetch_resource(Z_RES_P(zid), le_curl_name, le_curl)) == NULL) {
-               RETURN_FALSE;
-       }
+       ch = Z_CURL_P(zid);
 
        cp = curl_easy_duphandle(ch->cp);
        if (!cp) {
@@ -2095,24 +2208,19 @@ PHP_FUNCTION(curl_copy_handle)
                RETURN_FALSE;
        }
 
-       dupch = alloc_curl_handle();
+       dupch = init_curl_handle_into_zval(return_value);
        dupch->cp = cp;
 
        _php_setup_easy_copy_handlers(dupch, ch);
 
-#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */
        postfields = &ch->postfields;
        if (Z_TYPE_P(postfields) != IS_UNDEF) {
                if (build_mime_structure_from_hash(dupch, postfields) != SUCCESS) {
-                       _php_curl_close_ex(dupch);
+                       zval_ptr_dtor(return_value);
                        php_error_docref(NULL, E_WARNING, "Cannot rebuild mime structure");
                        RETURN_FALSE;
                }
        }
-#endif
-
-       ZVAL_RES(return_value, zend_register_resource(dupch, le_curl));
-       dupch->res = Z_RES_P(return_value);
 }
 /* }}} */
 
@@ -2742,8 +2850,8 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{
 
                case CURLOPT_SHARE:
                        {
-                               php_curlsh *sh;
-                               if ((sh = (php_curlsh *)zend_fetch_resource_ex(zvalue, le_curl_share_handle_name, le_curl_share_handle))) {
+                               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);
                                }
                        }
@@ -2773,7 +2881,7 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{
 }
 /* }}} */
 
-/* {{{ proto bool curl_setopt(resource ch, int option, mixed value)
+/* {{{ proto bool curl_setopt(CurlHandle ch, int option, mixed value)
    Set an option for a cURL transfer */
 PHP_FUNCTION(curl_setopt)
 {
@@ -2782,14 +2890,12 @@ PHP_FUNCTION(curl_setopt)
        php_curl   *ch;
 
        ZEND_PARSE_PARAMETERS_START(3, 3)
-               Z_PARAM_RESOURCE(zid)
+               Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
                Z_PARAM_LONG(options)
                Z_PARAM_ZVAL(zvalue)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((ch = (php_curl*)zend_fetch_resource(Z_RES_P(zid), le_curl_name, le_curl)) == NULL) {
-               RETURN_THROWS();
-       }
+       ch = Z_CURL_P(zid);
 
        if (options <= 0 && options != CURLOPT_SAFE_UPLOAD) {
                php_error_docref(NULL, E_WARNING, "Invalid curl configuration option");
@@ -2804,7 +2910,7 @@ PHP_FUNCTION(curl_setopt)
 }
 /* }}} */
 
-/* {{{ proto bool curl_setopt_array(resource ch, array options)
+/* {{{ proto bool curl_setopt_array(CurlHandle ch, array options)
    Set an array of option for a cURL transfer */
 PHP_FUNCTION(curl_setopt_array)
 {
@@ -2814,13 +2920,11 @@ PHP_FUNCTION(curl_setopt_array)
        zend_string     *string_key;
 
        ZEND_PARSE_PARAMETERS_START(2, 2)
-               Z_PARAM_RESOURCE(zid)
+               Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
                Z_PARAM_ARRAY(arr)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((ch = (php_curl*)zend_fetch_resource(Z_RES_P(zid), le_curl_name, le_curl)) == NULL) {
-               RETURN_THROWS();
-       }
+       ch = Z_CURL_P(zid);
 
        ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(arr), option, string_key, entry) {
                if (string_key) {
@@ -2853,7 +2957,7 @@ void _php_curl_cleanup_handle(php_curl *ch)
 }
 /* }}} */
 
-/* {{{ proto bool curl_exec(resource ch)
+/* {{{ proto bool curl_exec(CurlHandle ch)
    Perform a cURL session */
 PHP_FUNCTION(curl_exec)
 {
@@ -2862,12 +2966,10 @@ PHP_FUNCTION(curl_exec)
        php_curl        *ch;
 
        ZEND_PARSE_PARAMETERS_START(1, 1)
-               Z_PARAM_RESOURCE(zid)
+               Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((ch = (php_curl*)zend_fetch_resource(Z_RES_P(zid), le_curl_name, le_curl)) == NULL) {
-               RETURN_THROWS();
-       }
+       ch = Z_CURL_P(zid);
 
        _php_curl_verify_handlers(ch, 1);
 
@@ -2910,7 +3012,7 @@ PHP_FUNCTION(curl_exec)
 }
 /* }}} */
 
-/* {{{ proto mixed curl_getinfo(resource ch [, int option])
+/* {{{ proto mixed curl_getinfo(CurlHandle ch [, int option])
    Get information regarding a specific transfer */
 PHP_FUNCTION(curl_getinfo)
 {
@@ -2919,14 +3021,12 @@ PHP_FUNCTION(curl_getinfo)
        zend_long       option = 0;
 
        ZEND_PARSE_PARAMETERS_START(1, 2)
-               Z_PARAM_RESOURCE(zid)
+               Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
                Z_PARAM_OPTIONAL
                Z_PARAM_LONG(option)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((ch = (php_curl*)zend_fetch_resource(Z_RES_P(zid), le_curl_name, le_curl)) == NULL) {
-               RETURN_THROWS();
-       }
+       ch = Z_CURL_P(zid);
 
        if (ZEND_NUM_ARGS() < 2) {
                char *s_code;
@@ -3165,7 +3265,7 @@ PHP_FUNCTION(curl_getinfo)
 }
 /* }}} */
 
-/* {{{ proto string curl_error(resource ch)
+/* {{{ proto string curl_error(CurlHandle ch)
    Return a string contain the last error for the current session */
 PHP_FUNCTION(curl_error)
 {
@@ -3173,12 +3273,10 @@ PHP_FUNCTION(curl_error)
        php_curl        *ch;
 
        ZEND_PARSE_PARAMETERS_START(1, 1)
-               Z_PARAM_RESOURCE(zid)
+               Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((ch = (php_curl*)zend_fetch_resource(Z_RES_P(zid), le_curl_name, le_curl)) == NULL) {
-               RETURN_THROWS();
-       }
+       ch = Z_CURL_P(zid);
 
        if (ch->err.no) {
                ch->err.str[CURL_ERROR_SIZE] = 0;
@@ -3189,7 +3287,7 @@ PHP_FUNCTION(curl_error)
 }
 /* }}} */
 
-/* {{{ proto int curl_errno(resource ch)
+/* {{{ proto int curl_errno(CurlHandle ch)
    Return an integer containing the last error number */
 PHP_FUNCTION(curl_errno)
 {
@@ -3197,18 +3295,16 @@ PHP_FUNCTION(curl_errno)
        php_curl        *ch;
 
        ZEND_PARSE_PARAMETERS_START(1,1)
-               Z_PARAM_RESOURCE(zid)
+               Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((ch = (php_curl*)zend_fetch_resource(Z_RES_P(zid), le_curl_name, le_curl)) == NULL) {
-               RETURN_THROWS();
-       }
+       ch = Z_CURL_P(zid);
 
        RETURN_LONG(ch->err.no);
 }
 /* }}} */
 
-/* {{{ proto void curl_close(resource ch)
+/* {{{ proto void curl_close(CurlHandle ch)
    Close a cURL session */
 PHP_FUNCTION(curl_close)
 {
@@ -3216,26 +3312,22 @@ PHP_FUNCTION(curl_close)
        php_curl        *ch;
 
        ZEND_PARSE_PARAMETERS_START(1, 1)
-               Z_PARAM_RESOURCE(zid)
+               Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((ch = (php_curl*)zend_fetch_resource(Z_RES_P(zid), le_curl_name, le_curl)) == NULL) {
-               RETURN_THROWS();
-       }
+       ch = Z_CURL_P(zid);
 
        if (ch->in_callback) {
                php_error_docref(NULL, E_WARNING, "Attempt to close cURL handle from a callback");
                return;
        }
-
-       zend_list_close(Z_RES_P(zid));
 }
 /* }}} */
 
-/* {{{ _php_curl_close_ex()
-   List destructor for curl handles */
-static void _php_curl_close_ex(php_curl *ch)
+static void curl_free_obj(zend_object *object)
 {
+       php_curl *ch = curl_from_obj(object);
+
 #if PHP_CURL_DEBUG
        fprintf(stderr, "DTOR CALLED, ch = %x\n", ch);
 #endif
@@ -3299,19 +3391,9 @@ static void _php_curl_close_ex(php_curl *ch)
        }
 
        efree(ch->handlers);
-#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */
        zval_ptr_dtor(&ch->postfields);
-#endif
-       efree(ch);
-}
-/* }}} */
 
-/* {{{ _php_curl_close()
-   List destructor for curl handles */
-static void _php_curl_close(zend_resource *rsrc)
-{
-       php_curl *ch = (php_curl *) rsrc->ptr;
-       _php_curl_close_ex(ch);
+       zend_object_std_dtor(&ch->std);
 }
 /* }}} */
 
@@ -3381,7 +3463,7 @@ static void _php_curl_reset_handlers(php_curl *ch)
 }
 /* }}} */
 
-/* {{{ proto void curl_reset(resource ch)
+/* {{{ proto void curl_reset(CurlHandle ch)
    Reset all options of a libcurl session handle */
 PHP_FUNCTION(curl_reset)
 {
@@ -3389,12 +3471,10 @@ PHP_FUNCTION(curl_reset)
        php_curl   *ch;
 
        ZEND_PARSE_PARAMETERS_START(1, 1)
-               Z_PARAM_RESOURCE(zid)
+               Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((ch = (php_curl*)zend_fetch_resource(Z_RES_P(zid), le_curl_name, le_curl)) == NULL) {
-               RETURN_THROWS();
-       }
+       ch = Z_CURL_P(zid);
 
        if (ch->in_callback) {
                php_error_docref(NULL, E_WARNING, "Attempt to reset cURL handle from a callback");
@@ -3407,7 +3487,7 @@ PHP_FUNCTION(curl_reset)
 }
 /* }}} */
 
-/* {{{ proto void curl_escape(resource ch, string str)
+/* {{{ proto void curl_escape(CurlHandle ch, string str)
    URL encodes the given string */
 PHP_FUNCTION(curl_escape)
 {
@@ -3417,13 +3497,11 @@ PHP_FUNCTION(curl_escape)
        php_curl    *ch;
 
        ZEND_PARSE_PARAMETERS_START(2,2)
-               Z_PARAM_RESOURCE(zid)
+               Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
                Z_PARAM_STR(str)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((ch = (php_curl*)zend_fetch_resource(Z_RES_P(zid), le_curl_name, le_curl)) == NULL) {
-               RETURN_THROWS();
-       }
+       ch = Z_CURL_P(zid);
 
        if (ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(str))) {
                RETURN_FALSE;
@@ -3438,7 +3516,7 @@ PHP_FUNCTION(curl_escape)
 }
 /* }}} */
 
-/* {{{ proto void curl_unescape(resource ch, string str)
+/* {{{ proto void curl_unescape(CurlHandle ch, string str)
    URL decodes the given string */
 PHP_FUNCTION(curl_unescape)
 {
@@ -3449,13 +3527,11 @@ PHP_FUNCTION(curl_unescape)
        php_curl    *ch;
 
        ZEND_PARSE_PARAMETERS_START(2,2)
-               Z_PARAM_RESOURCE(zid)
+               Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
                Z_PARAM_STR(str)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((ch = (php_curl*)zend_fetch_resource(Z_RES_P(zid), le_curl_name, le_curl)) == NULL) {
-               RETURN_THROWS();
-       }
+       ch = Z_CURL_P(zid);
 
        if (ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(str))) {
                RETURN_FALSE;
@@ -3470,7 +3546,7 @@ PHP_FUNCTION(curl_unescape)
 }
 /* }}} */
 
-/* {{{ proto void curl_pause(resource ch, int bitmask)
+/* {{{ proto void curl_pause(CurlHandle ch, int bitmask)
        pause and unpause a connection */
 PHP_FUNCTION(curl_pause)
 {
@@ -3479,13 +3555,11 @@ PHP_FUNCTION(curl_pause)
        php_curl   *ch;
 
        ZEND_PARSE_PARAMETERS_START(2,2)
-               Z_PARAM_RESOURCE(zid)
+               Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)
                Z_PARAM_LONG(bitmask)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((ch = (php_curl*)zend_fetch_resource(Z_RES_P(zid), le_curl_name, le_curl)) == NULL) {
-               RETURN_THROWS();
-       }
+       ch = Z_CURL_P(zid);
 
        RETURN_LONG(curl_easy_pause(ch->cp, bitmask));
 }
index d8c1caac3c642d07c7336ff1e6687914ddab12df..6a4c48ee1852e4868d9875de1bd2e12e6d5326de 100644 (file)
@@ -21,6 +21,7 @@
 #endif
 
 #include "php.h"
+#include "Zend/zend_interfaces.h"
 
 #ifdef HAVE_CURL
 
 
 #define SAVE_CURLM_ERROR(__handle, __err) (__handle)->err.no = (int) __err;
 
-/* {{{ proto resource curl_multi_init(void)
+/* CurlMultiHandle class */
+
+static zend_class_entry *curl_multi_ce;
+
+static inline php_curlm *curl_multi_from_obj(zend_object *obj) {
+       return (php_curlm *)((char *)(obj) - XtOffsetOf(php_curlm, std));
+}
+
+#define Z_CURL_MULTI_P(zv) curl_multi_from_obj(Z_OBJ_P(zv))
+
+/* {{{ proto CurlMultiHandle curl_multi_init(void)
    Returns a new cURL multi handle */
 PHP_FUNCTION(curl_multi_init)
 {
@@ -55,17 +66,16 @@ PHP_FUNCTION(curl_multi_init)
 
        ZEND_PARSE_PARAMETERS_NONE();
 
-       mh = ecalloc(1, sizeof(php_curlm));
+       object_init_ex(return_value, curl_multi_ce);
+       mh = Z_CURL_MULTI_P(return_value);
        mh->multi = curl_multi_init();
        mh->handlers = ecalloc(1, sizeof(php_curlm_handlers));
 
        zend_llist_init(&mh->easyh, sizeof(zval), _php_curl_multi_cleanup_list, 0);
-
-       RETURN_RES(zend_register_resource(mh, le_curl_multi_handle));
 }
 /* }}} */
 
-/* {{{ proto int curl_multi_add_handle(resource mh, resource ch)
+/* {{{ proto int curl_multi_add_handle(CurlMultiHandle mh, Curl ch)
    Add a normal cURL handle to a cURL multi handle */
 PHP_FUNCTION(curl_multi_add_handle)
 {
@@ -76,23 +86,18 @@ PHP_FUNCTION(curl_multi_add_handle)
        CURLMcode error = CURLM_OK;
 
        ZEND_PARSE_PARAMETERS_START(2,2)
-               Z_PARAM_RESOURCE(z_mh)
-               Z_PARAM_RESOURCE(z_ch)
+               Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce)
+               Z_PARAM_OBJECT_OF_CLASS(z_ch, curl_ce)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
-               RETURN_THROWS();
-       }
-
-       if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(z_ch), le_curl_name, le_curl)) == NULL) {
-               RETURN_THROWS();
-       }
+       mh = Z_CURL_MULTI_P(z_mh);
+       ch = Z_CURL_P(z_ch);
 
        _php_curl_verify_handlers(ch, 1);
 
        _php_curl_cleanup_handle(ch);
 
-       GC_ADDREF(Z_RES_P(z_ch));
+       Z_ADDREF_P(z_ch);
        zend_llist_add_element(&mh->easyh, z_ch);
 
        error = curl_multi_add_handle(mh->multi, ch->cp);
@@ -105,28 +110,17 @@ PHP_FUNCTION(curl_multi_add_handle)
 void _php_curl_multi_cleanup_list(void *data) /* {{{ */
 {
        zval *z_ch = (zval *)data;
-       php_curl *ch;
 
-       if (!z_ch) {
-               return;
-       }
-       if (!Z_RES_P(z_ch)->ptr) {
-               return;
-       }
-       if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(z_ch), le_curl_name, le_curl)) == NULL) {
-               return;
-       }
-
-       zend_list_delete(Z_RES_P(z_ch));
+       zval_ptr_dtor(z_ch);
 }
 /* }}} */
 
 /* Used internally as comparison routine passed to zend_list_del_element */
-static int curl_compare_resources( zval *z1, zval *z2 ) /* {{{ */
+static int curl_compare_objects( zval *z1, zval *z2 ) /* {{{ */
 {
        return (Z_TYPE_P(z1) == Z_TYPE_P(z2) &&
-                       Z_TYPE_P(z1) == IS_RESOURCE &&
-                       Z_RES_P(z1) == Z_RES_P(z2));
+                       Z_TYPE_P(z1) == IS_OBJECT &&
+                       Z_OBJ_P(z1) == Z_OBJ_P(z2));
 }
 /* }}} */
 
@@ -139,10 +133,7 @@ static zval *_php_curl_multi_find_easy_handle(php_curlm *mh, CURL *easy) /* {{{
 
        for(pz_ch_temp = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch_temp;
                pz_ch_temp = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
-
-               if ((tmp_ch = (php_curl *)zend_fetch_resource(Z_RES_P(pz_ch_temp), le_curl_name, le_curl)) == NULL) {
-                       return NULL;
-               }
+               tmp_ch = Z_CURL_P(pz_ch_temp);
 
                if (tmp_ch->cp == easy) {
                        return pz_ch_temp;
@@ -153,7 +144,7 @@ static zval *_php_curl_multi_find_easy_handle(php_curlm *mh, CURL *easy) /* {{{
 }
 /* }}} */
 
-/* {{{ proto int curl_multi_remove_handle(resource mh, resource ch)
+/* {{{ proto int curl_multi_remove_handle(CurlMultiHandle mh, Curl ch)
    Remove a multi handle from a set of cURL handles */
 PHP_FUNCTION(curl_multi_remove_handle)
 {
@@ -164,28 +155,23 @@ PHP_FUNCTION(curl_multi_remove_handle)
        CURLMcode error = CURLM_OK;
 
        ZEND_PARSE_PARAMETERS_START(2,2)
-               Z_PARAM_RESOURCE(z_mh)
-               Z_PARAM_RESOURCE(z_ch)
+               Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce)
+               Z_PARAM_OBJECT_OF_CLASS(z_ch, curl_ce)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
-               RETURN_THROWS();
-       }
-
-       if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(z_ch), le_curl_name, le_curl)) == NULL) {
-               RETURN_THROWS();
-       }
+       mh = Z_CURL_MULTI_P(z_mh);
+       ch = Z_CURL_P(z_ch);
 
        error = curl_multi_remove_handle(mh->multi, ch->cp);
        SAVE_CURLM_ERROR(mh, error);
 
        RETVAL_LONG((zend_long) error);
-       zend_llist_del_element(&mh->easyh, z_ch, (int (*)(void *, void *))curl_compare_resources);
+       zend_llist_del_element(&mh->easyh, z_ch, (int (*)(void *, void *))curl_compare_objects);
 
 }
 /* }}} */
 
-/* {{{ proto int curl_multi_select(resource mh[, double timeout])
+/* {{{ proto int curl_multi_select(CurlMultiHandle mh[, double timeout])
    Get all the sockets associated with the cURL extension, which can then be "selected" */
 PHP_FUNCTION(curl_multi_select)
 {
@@ -196,14 +182,12 @@ PHP_FUNCTION(curl_multi_select)
        CURLMcode error = CURLM_OK;
 
        ZEND_PARSE_PARAMETERS_START(1,2)
-               Z_PARAM_RESOURCE(z_mh)
+               Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce)
                Z_PARAM_OPTIONAL
                Z_PARAM_DOUBLE(timeout)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
-               RETURN_THROWS();
-       }
+       mh = Z_CURL_MULTI_P(z_mh);
 
        error = curl_multi_wait(mh->multi, NULL, 0, (unsigned long) (timeout * 1000.0), &numfds);
        if (CURLM_OK != error) {
@@ -215,7 +199,7 @@ PHP_FUNCTION(curl_multi_select)
 }
 /* }}} */
 
-/* {{{ proto int curl_multi_exec(resource mh, int &still_running)
+/* {{{ proto int curl_multi_exec(CurlMultiHandle mh, int &still_running)
    Run the sub-connections of the current cURL handle */
 PHP_FUNCTION(curl_multi_exec)
 {
@@ -226,13 +210,11 @@ PHP_FUNCTION(curl_multi_exec)
        CURLMcode error = CURLM_OK;
 
        ZEND_PARSE_PARAMETERS_START(2, 2)
-               Z_PARAM_RESOURCE(z_mh)
+               Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce)
                Z_PARAM_ZVAL(z_still_running)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
-               RETURN_THROWS();
-       }
+       mh = Z_CURL_MULTI_P(z_mh);
 
        {
                zend_llist_position pos;
@@ -241,10 +223,7 @@ PHP_FUNCTION(curl_multi_exec)
 
                for (pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;
                        pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
-
-                       if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(pz_ch), le_curl_name, le_curl)) == NULL) {
-                               RETURN_THROWS();
-                       }
+                       ch = Z_CURL_P(pz_ch);
 
                        _php_curl_verify_handlers(ch, 1);
                }
@@ -259,20 +238,18 @@ PHP_FUNCTION(curl_multi_exec)
 }
 /* }}} */
 
-/* {{{ proto string curl_multi_getcontent(resource ch)
+/* {{{ proto string curl_multi_getcontent(CurlHandle ch)
    Return the content of a cURL handle if CURLOPT_RETURNTRANSFER is set */
 PHP_FUNCTION(curl_multi_getcontent)
 {
        zval     *z_ch;
        php_curl *ch;
 
-       ZEND_PARSE_PARAMETERS_START(1,1)
-               Z_PARAM_RESOURCE(z_ch)
+       ZEND_PARSE_PARAMETERS_START(1, 1)
+               Z_PARAM_OBJECT_OF_CLASS(z_ch, curl_ce)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(z_ch), le_curl_name, le_curl)) == NULL) {
-               RETURN_THROWS();
-       }
+       ch = Z_CURL_P(z_ch);
 
        if (ch->handlers->write->method == PHP_CURL_RETURN) {
                if (!ch->handlers->write->buf.s) {
@@ -286,7 +263,7 @@ PHP_FUNCTION(curl_multi_getcontent)
 }
 /* }}} */
 
-/* {{{ proto array curl_multi_info_read(resource mh [, int &msgs_in_queue])
+/* {{{ proto array curl_multi_info_read(CurlMultiHandle mh [, int &msgs_in_queue])
    Get information about the current transfers */
 PHP_FUNCTION(curl_multi_info_read)
 {
@@ -298,14 +275,12 @@ PHP_FUNCTION(curl_multi_info_read)
        php_curl  *ch;
 
        ZEND_PARSE_PARAMETERS_START(1, 2)
-               Z_PARAM_RESOURCE(z_mh)
+               Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce)
                Z_PARAM_OPTIONAL
                Z_PARAM_ZVAL(zmsgs_in_queue)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
-               RETURN_THROWS();
-       }
+       mh = Z_CURL_MULTI_P(z_mh);
 
        tmp_msg = curl_multi_info_read(mh->multi, &queued_msgs);
        if (tmp_msg == NULL) {
@@ -322,83 +297,46 @@ PHP_FUNCTION(curl_multi_info_read)
 
        /* find the original easy curl handle */
        {
-               zval    *pz_ch = _php_curl_multi_find_easy_handle(mh, tmp_msg->easy_handle);
+               zval *pz_ch = _php_curl_multi_find_easy_handle(mh, tmp_msg->easy_handle);
                if (pz_ch != NULL) {
-                       /* we are adding a reference to the underlying php_curl
-                          resource, so we need to add one to the resource's refcount
-                          in order to ensure it doesn't get destroyed when the
-                          underlying curl easy handle goes out of scope.
-                          Normally you would call zval_copy_ctor( pz_ch ), or
-                          SEPARATE_ZVAL, but those create new zvals, which is already
-                          being done in add_assoc_resource */
-                       Z_ADDREF_P(pz_ch);
-
                        /* we must save result to be able to read error message */
-                       ch = (php_curl*)zend_fetch_resource(Z_RES_P(pz_ch), le_curl_name, le_curl);
+                       ch = Z_CURL_P(pz_ch);
                        SAVE_CURL_ERROR(ch, tmp_msg->data.result);
 
-                       /* add_assoc_resource automatically creates a new zval to
-                          wrap the "resource" represented by the current pz_ch */
-
+                       Z_ADDREF_P(pz_ch);
                        add_assoc_zval(return_value, "handle", pz_ch);
                }
        }
 }
 /* }}} */
 
-/* {{{ proto void curl_multi_close(resource mh)
+/* {{{ proto void curl_multi_close(CurlMultiHandle mh)
    Close a set of cURL handles */
 PHP_FUNCTION(curl_multi_close)
 {
-       zval      *z_mh;
        php_curlm *mh;
+       zval *z_mh;
+
+       zend_llist_position pos;
+       zval *pz_ch;
 
        ZEND_PARSE_PARAMETERS_START(1,1)
-               Z_PARAM_RESOURCE(z_mh)
+               Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
-               RETURN_THROWS();
-       }
-
-       zend_list_close(Z_RES_P(z_mh));
-}
-/* }}} */
+       mh = Z_CURL_MULTI_P(z_mh);
 
-void _php_curl_multi_close(zend_resource *rsrc) /* {{{ */
-{
-       php_curlm *mh = (php_curlm *)rsrc->ptr;
-       if (mh) {
-               zend_llist_position pos;
-               php_curl *ch;
-               zval    *pz_ch;
-
-               for (pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;
-                       pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
-                       /* ptr is NULL means it already be freed */
-                       if (Z_RES_P(pz_ch)->ptr) {
-                               if ((ch = (php_curl *) zend_fetch_resource(Z_RES_P(pz_ch), le_curl_name, le_curl))) {
-                                       _php_curl_verify_handlers(ch, 0);
-                               }
-                       }
-               }
-
-               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 (mh->handlers) {
-                       efree(mh->handlers);
-               }
-               efree(mh);
-               rsrc->ptr = NULL;
+       for (pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;
+               pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
+               php_curl *ch = Z_CURL_P(pz_ch);
+               _php_curl_verify_handlers(ch, 1);
+               curl_multi_remove_handle(mh->multi, ch->cp);
        }
+       zend_llist_clean(&mh->easyh);
 }
 /* }}} */
 
-/* {{{ proto int curl_multi_errno(resource mh)
+/* {{{ proto int curl_multi_errno(CurlMultiHandle mh)
          Return an integer containing the last multi curl error number */
 PHP_FUNCTION(curl_multi_errno)
 {
@@ -406,12 +344,10 @@ PHP_FUNCTION(curl_multi_errno)
        php_curlm   *mh;
 
        ZEND_PARSE_PARAMETERS_START(1,1)
-               Z_PARAM_RESOURCE(z_mh)
+               Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
-               RETURN_THROWS();
-       }
+       mh = Z_CURL_MULTI_P(z_mh);
 
        RETURN_LONG(mh->err.no);
 }
@@ -450,7 +386,6 @@ static int _php_server_push_callback(CURL *parent_ch, CURL *easy, size_t num_hea
        zval                                    pz_ch;
        zval                                    headers;
        zval                                    retval;
-       zend_resource                   *res;
        char                                    *header;
        int                                     error;
        zend_fcall_info                 fci                     = empty_fcall_info;
@@ -460,18 +395,12 @@ static int _php_server_push_callback(CURL *parent_ch, CURL *easy, size_t num_hea
                return rval;
        }
 
-       parent = (php_curl*)zend_fetch_resource(Z_RES_P(pz_parent_ch), le_curl_name, le_curl);
+       parent = Z_CURL_P(pz_parent_ch);
 
-       ch = alloc_curl_handle();
+       ch = init_curl_handle_into_zval(&pz_ch);
        ch->cp = easy;
        _php_setup_easy_copy_handlers(ch, parent);
 
-       Z_ADDREF_P(pz_parent_ch);
-
-       res = zend_register_resource(ch, le_curl);
-       ch->res = res;
-       ZVAL_RES(&pz_ch, res);
-
        size_t i;
        array_init(&headers);
        for(i=0; i<num_headers; i++) {
@@ -499,7 +428,6 @@ static int _php_server_push_callback(CURL *parent_ch, CURL *easy, size_t num_hea
        } else if (!Z_ISUNDEF(retval)) {
                if (CURL_PUSH_DENY != zval_get_long(&retval)) {
                    rval = CURL_PUSH_OK;
-                       GC_ADDREF(Z_RES(pz_ch));
                        zend_llist_add_element(&mh->easyh, &pz_ch);
                } else {
                        /* libcurl will free this easy handle, avoid double free */
@@ -570,7 +498,7 @@ static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue,
 }
 /* }}} */
 
-/* {{{ proto int curl_multi_setopt(resource mh, int option, mixed value)
+/* {{{ proto int curl_multi_setopt(CurlMultiHandle mh, int option, mixed value)
        Set an option for the curl multi handle */
 PHP_FUNCTION(curl_multi_setopt)
 {
@@ -579,14 +507,12 @@ PHP_FUNCTION(curl_multi_setopt)
        php_curlm *mh;
 
        ZEND_PARSE_PARAMETERS_START(3,3)
-               Z_PARAM_RESOURCE(z_mh)
+               Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce)
                Z_PARAM_LONG(options)
                Z_PARAM_ZVAL(zvalue)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
-               RETURN_THROWS();
-       }
+       mh = Z_CURL_MULTI_P(z_mh);
 
        if (!_php_curl_multi_setopt(mh, options, zvalue, return_value)) {
                RETURN_TRUE;
@@ -596,4 +522,92 @@ PHP_FUNCTION(curl_multi_setopt)
 }
 /* }}} */
 
+/* CurlMultiHandle class */
+
+static zend_object_handlers curl_multi_handlers;
+
+static zend_object *curl_multi_create_object(zend_class_entry *class_type) {
+       php_curlm *intern = zend_object_alloc(sizeof(php_curlm), class_type);
+
+       zend_object_std_init(&intern->std, class_type);
+       object_properties_init(&intern->std, class_type);
+       intern->std.handlers = &curl_multi_handlers;
+
+       return &intern->std;
+}
+
+static zend_function *curl_multi_get_constructor(zend_object *object) {
+       zend_throw_error(NULL, "Cannot directly construct CurlMultiHandle, use curl_multi_init() instead");
+       return NULL;
+}
+
+void curl_multi_free_obj(zend_object *object)
+{
+       php_curlm *mh = curl_multi_from_obj(object);
+
+       zend_llist_position pos;
+       php_curl *ch;
+       zval    *pz_ch;
+
+       for (pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;
+               pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
+               if (!(OBJ_FLAGS(Z_OBJ_P(pz_ch)) & IS_OBJ_FREE_CALLED)) {
+                       ch = Z_CURL_P(pz_ch);
+                       _php_curl_verify_handlers(ch, 0);
+               }
+       }
+
+       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 (mh->handlers) {
+               efree(mh->handlers);
+       }
+
+       zend_object_std_dtor(&mh->std);
+}
+
+static HashTable *curl_multi_get_gc(zend_object *object, zval **table, int *n)
+{
+       php_curlm *curl_multi = curl_multi_from_obj(object);
+
+       zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
+
+       if (curl_multi->handlers) {
+               if (curl_multi->handlers->server_push) {
+                       zend_get_gc_buffer_add_zval(gc_buffer, &curl_multi->handlers->server_push->func_name);
+               }
+       }
+
+       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)) {
+               zend_get_gc_buffer_add_zval(gc_buffer, pz_ch);
+       }
+
+       zend_get_gc_buffer_use(gc_buffer, table, n);
+
+       return zend_std_get_properties(object);
+}
+
+void curl_multi_register_class(const zend_function_entry *method_entries) {
+       zend_class_entry ce_multi;
+       INIT_CLASS_ENTRY(ce_multi, "CurlMultiHandle", method_entries);
+       curl_multi_ce = zend_register_internal_class(&ce_multi);
+       curl_multi_ce->ce_flags |= ZEND_ACC_FINAL;
+       curl_multi_ce->create_object = curl_multi_create_object;
+       curl_multi_ce->serialize = zend_class_serialize_deny;
+       curl_multi_ce->unserialize = zend_class_unserialize_deny;
+
+       memcpy(&curl_multi_handlers, &std_object_handlers, sizeof(zend_object_handlers));
+       curl_multi_handlers.offset = XtOffsetOf(php_curlm, std);
+       curl_multi_handlers.free_obj = curl_multi_free_obj;
+       curl_multi_handlers.get_gc = curl_multi_get_gc;
+       curl_multi_handlers.get_constructor = curl_multi_get_constructor;
+       curl_multi_handlers.clone_obj = NULL;
+}
+
 #endif
index 2013e1cef2f9da2805cc7e1ada0f982c3ebbe7ec..a469d5e41cf126926c93d4e3846073f73b24a352 100644 (file)
@@ -59,22 +59,10 @@ extern zend_module_entry curl_module_entry;
 #define SAVE_CURL_ERROR(__handle, __err) \
     do { (__handle)->err.no = (int) __err; } while (0)
 
-extern int  le_curl;
-#define le_curl_name "cURL handle"
-extern int  le_curl_multi_handle;
-#define le_curl_multi_handle_name "cURL Multi Handle"
-extern int  le_curl_share_handle;
-#define le_curl_share_handle_name "cURL Share Handle"
-//extern int  le_curl_pushheaders;
-//#define le_curl_pushheaders "cURL Push Headers"
-
 PHP_MINIT_FUNCTION(curl);
 PHP_MSHUTDOWN_FUNCTION(curl);
 PHP_MINFO_FUNCTION(curl);
 
-void _php_curl_multi_close(zend_resource *);
-void _php_curl_share_close(zend_resource *);
-
 typedef struct {
        zval                  func_name;
        zend_fcall_info_cache fci_cache;
@@ -129,15 +117,13 @@ struct _php_curl_free {
 typedef struct {
        CURL                         *cp;
        php_curl_handlers            *handlers;
-       zend_resource                *res;
        struct _php_curl_free        *to_free;
        struct _php_curl_send_headers header;
        struct _php_curl_error        err;
        zend_bool                     in_callback;
        uint32_t*                     clone;
-#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */
        zval                          postfields;
-#endif
+       zend_object                   std;
 } php_curl;
 
 #define CURLOPT_SAFE_UPLOAD -1
@@ -154,6 +140,7 @@ typedef struct {
        struct {
                int no;
        } err;
+       zend_object std;
 } php_curlm;
 
 typedef struct {
@@ -161,15 +148,35 @@ typedef struct {
        struct {
                int no;
        } err;
+       zend_object std;
 } php_curlsh;
 
-php_curl *alloc_curl_handle();
+php_curl *init_curl_handle_into_zval(zval *curl);
+void init_curl_handle(php_curl *ch);
 void _php_curl_cleanup_handle(php_curl *);
 void _php_curl_multi_cleanup_list(void *data);
 void _php_curl_verify_handlers(php_curl *ch, int reporterror);
 void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source);
 
+static inline php_curl *curl_from_obj(zend_object *obj) {
+       return (php_curl *)((char *)(obj) - XtOffsetOf(php_curl, std));
+}
+
+#define Z_CURL_P(zv) curl_from_obj(Z_OBJ_P(zv))
+
+static inline php_curlsh *curl_share_from_obj(zend_object *obj) {
+       return (php_curlsh *)((char *)(obj) - XtOffsetOf(php_curlsh, std));
+}
+
+#define Z_CURL_SHARE_P(zv) curl_share_from_obj(Z_OBJ_P(zv))
+
+PHP_CURL_API extern zend_class_entry *curl_ce;
+PHP_CURL_API extern zend_class_entry *curl_share_ce;
+
+void curl_multi_register_class(const zend_function_entry *method_entries);
+void curl_share_register_class(const zend_function_entry *method_entries);
 void curlfile_register_class(void);
+
 PHP_CURL_API extern zend_class_entry *curl_CURLFile_class;
 
 #else
index 78532f4d37f2901e0c77c494f0daa4c573f76146..c8ba8e3270be8afa991a3df0e6d05e44fdbbaec2 100644 (file)
@@ -21,6 +21,7 @@
 #endif
 
 #include "php.h"
+#include "Zend/zend_interfaces.h"
 
 #ifdef HAVE_CURL
 
@@ -30,7 +31,9 @@
 
 #define SAVE_CURLSH_ERROR(__handle, __err) (__handle)->err.no = (int) __err;
 
-/* {{{ proto void curl_share_init()
+zend_class_entry *curl_share_ce;
+
+/* {{{ proto CurlShareHandle curl_share_init()
    Initialize a share curl handle */
 PHP_FUNCTION(curl_share_init)
 {
@@ -38,30 +41,22 @@ PHP_FUNCTION(curl_share_init)
 
        ZEND_PARSE_PARAMETERS_NONE();
 
-       sh = ecalloc(1, sizeof(php_curlsh));
+       object_init_ex(return_value, curl_share_ce);
+       sh = Z_CURL_SHARE_P(return_value);
 
        sh->share = curl_share_init();
-
-       RETURN_RES(zend_register_resource(sh, le_curl_share_handle));
 }
 /* }}} */
 
-/* {{{ proto void curl_share_close(resource sh)
+/* {{{ proto void curl_share_close(CurlShareHandle sh)
    Close a set of cURL handles */
 PHP_FUNCTION(curl_share_close)
 {
        zval *z_sh;
-       php_curlsh *sh;
 
        ZEND_PARSE_PARAMETERS_START(1,1)
-               Z_PARAM_RESOURCE(z_sh)
+               Z_PARAM_OBJECT_OF_CLASS(z_sh, curl_share_ce)
        ZEND_PARSE_PARAMETERS_END();
-
-       if ((sh = (php_curlsh *)zend_fetch_resource(Z_RES_P(z_sh), le_curl_share_handle_name, le_curl_share_handle)) == NULL) {
-               RETURN_THROWS();
-       }
-
-       zend_list_close(Z_RES_P(z_sh));
 }
 /* }}} */
 
@@ -87,23 +82,21 @@ static int _php_curl_share_setopt(php_curlsh *sh, zend_long option, zval *zvalue
 }
 /* }}} */
 
-/* {{{ proto bool curl_share_setopt(resource sh, int option, mixed value)
+/* {{{ proto bool curl_share_setopt(CurlShareHandle sh, int option, mixed value)
       Set an option for a cURL transfer */
 PHP_FUNCTION(curl_share_setopt)
 {
-       zval       *zid, *zvalue;
+       zval       *z_sh, *zvalue;
        zend_long        options;
        php_curlsh *sh;
 
        ZEND_PARSE_PARAMETERS_START(3,3)
-               Z_PARAM_RESOURCE(zid)
+               Z_PARAM_OBJECT_OF_CLASS(z_sh, curl_share_ce)
                Z_PARAM_LONG(options)
                Z_PARAM_ZVAL(zvalue)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((sh = (php_curlsh *)zend_fetch_resource(Z_RES_P(zid), le_curl_share_handle_name, le_curl_share_handle)) == NULL) {
-               RETURN_THROWS();
-       }
+       sh = Z_CURL_SHARE_P(z_sh);
 
        if (!_php_curl_share_setopt(sh, options, zvalue, return_value)) {
                RETURN_TRUE;
@@ -113,18 +106,7 @@ PHP_FUNCTION(curl_share_setopt)
 }
 /* }}} */
 
-void _php_curl_share_close(zend_resource *rsrc) /* {{{ */
-{
-       php_curlsh *sh = (php_curlsh *)rsrc->ptr;
-       if (sh) {
-               curl_share_cleanup(sh->share);
-               efree(sh);
-               rsrc->ptr = NULL;
-       }
-}
-/* }}} */
-
-/* {{{ proto int curl_share_errno(resource mh)
+/* {{{ proto int curl_share_errno(CurlShareHandle sh)
          Return an integer containing the last share curl error number */
 PHP_FUNCTION(curl_share_errno)
 {
@@ -132,12 +114,10 @@ PHP_FUNCTION(curl_share_errno)
        php_curlsh  *sh;
 
        ZEND_PARSE_PARAMETERS_START(1,1)
-               Z_PARAM_RESOURCE(z_sh)
+               Z_PARAM_OBJECT_OF_CLASS(z_sh, curl_share_ce)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((sh = (php_curlsh *)zend_fetch_resource(Z_RES_P(z_sh), le_curl_share_handle_name, le_curl_share_handle)) == NULL) {
-               RETURN_THROWS();
-       }
+       sh = Z_CURL_SHARE_P(z_sh);
 
        RETURN_LONG(sh->err.no);
 }
@@ -164,4 +144,47 @@ PHP_FUNCTION(curl_share_strerror)
 }
 /* }}} */
 
+/* CurlShareHandle class */
+
+static zend_object_handlers curl_share_handlers;
+
+static zend_object *curl_share_create_object(zend_class_entry *class_type) {
+       php_curlsh *intern = zend_object_alloc(sizeof(php_curlsh), class_type);
+
+       zend_object_std_init(&intern->std, class_type);
+       object_properties_init(&intern->std, class_type);
+       intern->std.handlers = &curl_share_handlers;
+
+       return &intern->std;
+}
+
+static zend_function *curl_share_get_constructor(zend_object *object) {
+       zend_throw_error(NULL, "Cannot directly construct CurlShareHandle, use curl_share_init() instead");
+       return NULL;
+}
+
+void curl_share_free_obj(zend_object *object)
+{
+       php_curlsh *sh = curl_share_from_obj(object);
+
+       curl_share_cleanup(sh->share);
+       zend_object_std_dtor(&sh->std);
+}
+
+void curl_share_register_class(const zend_function_entry *method_entries) {
+       zend_class_entry ce_share;
+       INIT_CLASS_ENTRY(ce_share, "CurlShareHandle", method_entries);
+       curl_share_ce = zend_register_internal_class(&ce_share);
+       curl_share_ce->ce_flags |= ZEND_ACC_FINAL;
+       curl_share_ce->create_object = curl_share_create_object;
+       curl_share_ce->serialize = &zend_class_serialize_deny;
+       curl_share_ce->unserialize = &zend_class_unserialize_deny;
+
+       memcpy(&curl_share_handlers, &std_object_handlers, sizeof(zend_object_handlers));
+       curl_share_handlers.offset = XtOffsetOf(php_curlsh, std);
+       curl_share_handlers.free_obj = curl_share_free_obj;
+       curl_share_handlers.get_constructor = curl_share_get_constructor;
+       curl_share_handlers.clone_obj = NULL;
+}
+
 #endif
index 66b81097ea29bffc44d2614110f238a2c92d51f4..759e4bc1be16385ae8d9d5c01c4df2fbc7c07104 100644 (file)
@@ -13,15 +13,17 @@ if (!extension_loaded('curl')) {
 
 $ch1 = curl_init();
 var_dump($ch1);
-var_dump(get_resource_type($ch1));
+var_dump($ch1::class);
 
 $ch2 = curl_multi_init();
 var_dump($ch2);
-var_dump(get_resource_type($ch2));
+var_dump($ch2::class);
 
 ?>
---EXPECTF--
-resource(%d) of type (curl)
-string(4) "curl"
-resource(%d) of type (curl_multi)
-string(10) "curl_multi"
+--EXPECT--
+object(CurlHandle)#1 (0) {
+}
+string(10) "CurlHandle"
+object(CurlMultiHandle)#2 (0) {
+}
+string(15) "CurlMultiHandle"
index 01564c0a585fdd06c27b03c8eb2b43b832dd0d6c..bf04d9faea501df535745735f2bb7fb83a4229b9 100644 (file)
@@ -27,7 +27,11 @@ resource(%d) of type (stream)
 resource(%d) of type (stream)
 resource(%d) of type (Unknown)
 resource(%d) of type (Unknown)
-resource(%d) of type (curl)
-resource(%d) of type (curl)
-resource(%d) of type (Unknown)
-resource(%d) of type (Unknown)
+object(CurlHandle)#1 (0) {
+}
+object(CurlHandle)#1 (0) {
+}
+object(CurlHandle)#1 (0) {
+}
+object(CurlHandle)#1 (0) {
+}
index 4505352ca8e4d0dd3eddbc53dbcebb064d1d84de..95eec344a894ef9e3007e8ce6f4de3f9c505be22 100644 (file)
@@ -53,7 +53,8 @@ class MyHttpClient
                 if (CURLMSG_DONE !== $info['msg']) {
                     continue;
                 }
-                die("Start handle request.");
+                echo "Start handle request.\n";
+                return;
             }
         }
     }
@@ -72,6 +73,7 @@ class MyHttpClient
 
 $buzz = new MyHttpClient();
 $buzz->sendRequest();
+?>
 --EXPECT--
 Start handle request.
 
index 88605e648f6be98d8298336034778f82d35f24dc..88d65d316cbbb24c5138388dfb4c8c4a149459f3 100644 (file)
@@ -9,5 +9,6 @@ Jean-Marc Fontaine <jmf@durcommefaire.net>
   $ch = curl_init();
   var_dump($ch);
 ?>
---EXPECTF--
-resource(%d) of type (curl)
+--EXPECT--
+object(CurlHandle)#1 (0) {
+}
index 5824fdddb80b6690416ae12a356f69701b4f23e5..af7d9ea88edfb91af328403e4abd1e738dc153b1 100644 (file)
@@ -13,5 +13,6 @@ $ch = curl_init();
 curl_close($ch);
 var_dump($ch);
 ?>
---EXPECTF--
-resource(%d) of type (Unknown)
+--EXPECT--
+object(CurlHandle)#1 (0) {
+}
diff --git a/ext/curl/tests/curl_handle_clone.phpt b/ext/curl/tests/curl_handle_clone.phpt
new file mode 100644 (file)
index 0000000..aa1251c
--- /dev/null
@@ -0,0 +1,26 @@
+--TEST--
+Test that cloning of Curl objects is supported
+--SKIPIF--
+<?php include 'skipif.inc'; ?>
+--FILE--
+<?php
+
+include 'server.inc';
+$host = curl_cli_server_start();
+
+$ch1 = curl_init();
+curl_setopt($ch1, CURLOPT_URL, $host);
+curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1);
+curl_exec($ch1);
+
+$ch2 = clone $ch1;
+curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 0);
+
+var_dump(curl_getinfo($ch1, CURLINFO_EFFECTIVE_URL) === curl_getinfo($ch2, CURLINFO_EFFECTIVE_URL));
+curl_exec($ch2);
+
+?>
+--EXPECT--
+bool(true)
+Hello World!
+Hello World!
index a9712dcf150b4f4f3d69e913b11d9a4d5cc87610..76dcb5882650a89af2a5f5d4b3ee68981628d2b5 100644 (file)
@@ -13,5 +13,6 @@ $ch = curl_multi_init();
 curl_multi_close($ch);
 var_dump($ch);
 ?>
---EXPECTF--
-resource(%d) of type (Unknown)
+--EXPECT--
+object(CurlMultiHandle)#1 (0) {
+}
index 42deaf78be14803fa59e753069b062a70ccddcc2..f47028e8733783c167088f754b15cb173d900319 100644 (file)
@@ -11,15 +11,11 @@ var_dump($cmh);
 $multi_close_result = curl_multi_close($cmh);
 var_dump($multi_close_result);
 var_dump($cmh);
-try {
-    $bad_mh_close_result = curl_multi_close($cmh);
-    var_dump($bad_mh_close_result);
-} catch (TypeError $e) {
-    echo $e->getMessage(), "\n";
-}
+curl_multi_close($cmh);
 ?>
 --EXPECTF--
-resource(%d) of type (curl_multi)
+object(CurlMultiHandle)#1 (0) {
+}
 NULL
-resource(%d) of type (Unknown)
-curl_multi_close(): supplied resource is not a valid cURL Multi Handle resource
+object(CurlMultiHandle)#1 (0) {
+}
index 9f54e10b1285cf212e09933797abd98100db4131..bbaa1cec2525322ac0997ad7a5f1a2d11df31ddd 100644 (file)
@@ -39,7 +39,8 @@ array(3) {
   ["result"]=>
   int(%d)
   ["handle"]=>
-  resource(%d) of type (curl)
+  object(CurlHandle)#%d (0) {
+  }
 }
 array(3) {
   ["msg"]=>
@@ -47,5 +48,6 @@ array(3) {
   ["result"]=>
   int(%d)
   ["handle"]=>
-  resource(%d) of type (curl)
+  object(CurlHandle)#%d (0) {
+  }
 }
index 452bc42837c460021625294a5586ff2e1c8d0c2e..808a8c4814d3e16a9c7e884fbdf3277dfc03ea78 100644 (file)
@@ -25,5 +25,7 @@ var_dump($mh);
 ?>
 --EXPECTF--
 *** Testing curl_multi_init(void); ***
-resource(%d) of type (curl_multi)
-resource(%d) of type (Unknown)
+object(CurlMultiHandle)#1 (0) {
+}
+object(CurlMultiHandle)#1 (0) {
+}
index eec1bde0996256e7add60131844cb0fa2b415b09..4c2c07d73dadbe4e4d41220cf8f4259bbc2255af 100644 (file)
@@ -6,14 +6,15 @@ curl_share_close basic test
 <?php
 
 $sh = curl_share_init();
-//Show that there's a curl_share resource
+//Show that there's a curl_share object
 var_dump($sh);
 
 curl_share_close($sh);
-//Show that resource is no longer a curl_share, and is therefore unusable and "closed"
 var_dump($sh);
 
 ?>
---EXPECTF--
-resource(%d) of type (curl_share)
-resource(%d) of type (Unknown)
+--EXPECT--
+object(CurlShareHandle)#1 (0) {
+}
+object(CurlShareHandle)#1 (0) {
+}
index 3b9ebf75ab53514b898caf62f0ebc4459dd8ca9d..ff985fc63fae619bf329a1ee82f9eee3e1c4781b 100644 (file)
@@ -510,8 +510,8 @@ static const func_info_t func_infos[] = {
        F1("mysqli_use_result",                                         MAY_BE_FALSE | MAY_BE_OBJECT),
 
        /* ext/curl */
-       F1("curl_init",                             MAY_BE_FALSE | MAY_BE_RESOURCE),
-       F1("curl_copy_handle",                      MAY_BE_FALSE | MAY_BE_RESOURCE),
+       F1("curl_init",                             MAY_BE_FALSE | MAY_BE_OBJECT),
+       F1("curl_copy_handle",                      MAY_BE_FALSE | MAY_BE_OBJECT),
        F1("curl_version",                          MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
        F1("curl_getinfo",                          MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
        F1("curl_error",                            MAY_BE_STRING),
@@ -519,9 +519,9 @@ static const func_info_t func_infos[] = {
        F1("curl_multi_strerror",                   MAY_BE_NULL | MAY_BE_STRING),
        F1("curl_escape",                           MAY_BE_FALSE | MAY_BE_STRING),
        F1("curl_unescape",                         MAY_BE_FALSE | MAY_BE_STRING),
-       F1("curl_multi_init",                       MAY_BE_RESOURCE),
-       F1("curl_multi_info_read",                  MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_RESOURCE),
-       F1("curl_share_init",                       MAY_BE_RESOURCE),
+       F1("curl_multi_init",                       MAY_BE_OBJECT),
+       F1("curl_multi_info_read",                  MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_OBJECT),
+       F1("curl_share_init",                       MAY_BE_OBJECT),
        F1("curl_file_create",                      MAY_BE_OBJECT),
 
        /* ext/mbstring */
index 92cf671c1dd50a4ff283d2f7cf953659bf874029..8d10ce5497757b0b9c62edb3e40228ab4fd1c007 100644 (file)
@@ -12,16 +12,15 @@ PHP Testfest Berlin 2009-05-10
        if (!extension_loaded('posix')) {
         die('SKIP - POSIX extension not available');
     }
-
-    if (!function_exists('curl_init')) {
-        die('SKIP - Function curl_init() not available');
+       if (!extension_loaded('sockets')) {
+        die('SKIP - Sockets extension not available');
     }
 ?>
 --FILE--
 <?php
 var_dump(posix_ttyname(0)); // param not a resource
 try {
-    var_dump(posix_ttyname(curl_init())); // wrong resource type
+    var_dump(posix_ttyname(socket_create(AF_INET, SOCK_DGRAM, SOL_UDP))); // wrong resource type
 } catch (TypeError $e) {
     echo $e->getMessage(), "\n";
 }