2 +--------------------------------------------------------------------+
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2014, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
13 #include "php_http_api.h"
15 #include "php_network.h"
16 #include "zend_closures.h"
18 #if PHP_HTTP_HAVE_LIBCURL
20 typedef struct php_http_client_curl_user_ev
{
22 php_http_client_curl_user_context_t
*context
;
23 } php_http_client_curl_user_ev_t
;
25 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(ai_user_handler
, 0, 1, IS_LONG
, 1)
26 ZEND_ARG_OBJ_INFO(0, client
, "http\\Client", 0)
27 ZEND_ARG_TYPE_INFO(0, stream
, IS_RESOURCE
, 1)
28 ZEND_ARG_TYPE_INFO(0, action
, IS_LONG
, 1)
30 static ZEND_NAMED_FUNCTION(php_http_client_curl_user_handler
)
32 zval
*zstream
= NULL
, *zclient
= NULL
;
33 php_stream
*stream
= NULL
;
35 php_socket_t fd
= CURL_SOCKET_TIMEOUT
;
36 php_http_client_object_t
*client
= NULL
;
37 php_http_client_curl_t
*curl
;
39 if (SUCCESS
!= zend_parse_parameters(ZEND_NUM_ARGS(), "O|rl", &zclient
, php_http_client_get_class_entry(), &zstream
, &action
)) {
43 client
= PHP_HTTP_OBJ(NULL
, zclient
);
45 php_stream_from_zval(stream
, zstream
);
47 if (SUCCESS
!= php_stream_cast(stream
, PHP_STREAM_AS_SOCKETD
, (void *) &fd
, 1)) {
51 php_http_client_curl_loop(client
->client
, fd
, action
);
53 curl
= client
->client
->ctx
;
54 RETVAL_LONG(curl
->unfinished
);
57 static void php_http_client_curl_user_timer(CURLM
*multi
, long timeout_ms
, void *timer_data
)
59 php_http_client_curl_user_context_t
*context
= timer_data
;
62 fprintf(stderr
, "\ntimer <- timeout_ms: %ld\n", timeout_ms
);
65 if (timeout_ms
>= 0) {
66 zval args
[1], *ztimeout
= &args
[0];
68 ZVAL_LONG(ztimeout
, timeout_ms
);
69 php_http_object_method_call(&context
->timer
, &context
->user
, NULL
, 1, args
);
70 zval_ptr_dtor(ztimeout
);
74 static int php_http_client_curl_user_socket(CURL
*easy
, curl_socket_t sock
, int action
, void *socket_data
, void *assign_data
)
76 php_http_client_curl_user_context_t
*ctx
= socket_data
;
77 php_http_client_curl_t
*curl
= ctx
->client
->ctx
;
78 php_http_client_curl_user_ev_t
*ev
= assign_data
;
79 zval args
[2], *zaction
= &args
[1], *zsocket
= &args
[0];
86 ev
= ecalloc(1, sizeof(*ev
));
88 ev
->socket
= php_stream_sock_open_from_socket(sock
, NULL
);
90 curl_multi_assign(curl
->handle
->multi
, sock
, ev
);
97 case CURL_POLL_REMOVE
:
99 php_stream_to_zval(ev
->socket
, zsocket
);
100 Z_TRY_ADDREF_P(zsocket
);
101 ZVAL_LONG(zaction
, action
);
102 php_http_object_method_call(&ctx
->socket
, &ctx
->user
, NULL
, 2, args
);
103 zval_ptr_dtor(zsocket
);
104 zval_ptr_dtor(zaction
);
108 php_error_docref(NULL
, E_WARNING
, "Unknown socket action %d", action
);
112 if (action
== CURL_POLL_REMOVE
) {
113 php_stream_close(ev
->socket
);
115 curl_multi_assign(curl
->handle
->multi
, sock
, NULL
);
120 static ZEND_RESULT_CODE
php_http_client_curl_user_once(void *context
)
122 php_http_client_curl_user_context_t
*ctx
= context
;
125 fprintf(stderr
, "O");
128 return php_http_object_method_call(&ctx
->once
, &ctx
->user
, NULL
, 0, NULL
);
131 static ZEND_RESULT_CODE
php_http_client_curl_user_wait(void *context
, struct timeval
*custom_timeout
)
133 php_http_client_curl_user_context_t
*ctx
= context
;
134 struct timeval timeout
;
135 zval args
[1], *ztimeout
= &args
[0];
139 fprintf(stderr
, "W");
142 if (!custom_timeout
|| !timerisset(custom_timeout
)) {
143 php_http_client_curl_get_timeout(ctx
->client
->ctx
, 1000, &timeout
);
144 custom_timeout
= &timeout
;
147 ZVAL_LONG(ztimeout
, custom_timeout
->tv_sec
* 1000 + custom_timeout
->tv_usec
/ 1000);
148 rv
= php_http_object_method_call(&ctx
->wait
, &ctx
->user
, NULL
, 1, args
);
149 zval_ptr_dtor(ztimeout
);
154 static ZEND_RESULT_CODE
php_http_client_curl_user_exec(void *context
)
156 php_http_client_curl_user_context_t
*ctx
= context
;
157 php_http_client_curl_t
*curl
= ctx
->client
->ctx
;
160 fprintf(stderr
, "E");
164 php_http_client_curl_loop(ctx
->client
, CURL_SOCKET_TIMEOUT
, 0);
167 if (SUCCESS
!= php_http_object_method_call(&ctx
->send
, &ctx
->user
, NULL
, 0, NULL
)) {
170 } while (curl
->unfinished
&& !EG(exception
));
175 static void *php_http_client_curl_user_init(php_http_client_t
*client
, void *user_data
)
177 php_http_client_curl_t
*curl
= client
->ctx
;
178 php_http_client_curl_user_context_t
*ctx
;
179 php_http_object_method_t init
;
180 zval args
[1], *zclosure
= &args
[0];
183 fprintf(stderr
, "I");
186 ctx
= ecalloc(1, sizeof(*ctx
));
187 ctx
->client
= client
;
188 ZVAL_COPY(&ctx
->user
, user_data
);
190 memset(&ctx
->closure
, 0, sizeof(ctx
->closure
));
191 ctx
->closure
.common
.type
= ZEND_INTERNAL_FUNCTION
;
192 ctx
->closure
.common
.function_name
= zend_string_init(ZEND_STRL("php_http_client_curl_user_handler"), 0);
193 ctx
->closure
.internal_function
.handler
= php_http_client_curl_user_handler
;
194 ctx
->closure
.internal_function
.arg_info
= (zend_internal_arg_info
*) &ai_user_handler
[1];
195 ctx
->closure
.internal_function
.num_args
= 3;
196 ctx
->closure
.internal_function
.required_num_args
= 1;
199 zend_create_closure(zclosure
, &ctx
->closure
, NULL
, NULL
, NULL
);
201 php_http_object_method_init(&init
, &ctx
->user
, ZEND_STRL("init"));
202 php_http_object_method_call(&init
, &ctx
->user
, NULL
, 1, args
);
203 php_http_object_method_dtor(&init
);
204 zval_ptr_dtor(zclosure
);
206 php_http_object_method_init(&ctx
->timer
, &ctx
->user
, ZEND_STRL("timer"));
207 php_http_object_method_init(&ctx
->socket
, &ctx
->user
, ZEND_STRL("socket"));
208 php_http_object_method_init(&ctx
->once
, &ctx
->user
, ZEND_STRL("once"));
209 php_http_object_method_init(&ctx
->wait
, &ctx
->user
, ZEND_STRL("wait"));
210 php_http_object_method_init(&ctx
->send
, &ctx
->user
, ZEND_STRL("send"));
212 curl_multi_setopt(curl
->handle
->multi
, CURLMOPT_SOCKETDATA
, ctx
);
213 curl_multi_setopt(curl
->handle
->multi
, CURLMOPT_SOCKETFUNCTION
, php_http_client_curl_user_socket
);
214 curl_multi_setopt(curl
->handle
->multi
, CURLMOPT_TIMERDATA
, ctx
);
215 curl_multi_setopt(curl
->handle
->multi
, CURLMOPT_TIMERFUNCTION
, php_http_client_curl_user_timer
);
220 static void php_http_client_curl_user_dtor(void **context
)
222 php_http_client_curl_user_context_t
*ctx
= *context
;
223 php_http_client_curl_t
*curl
;
226 fprintf(stderr
, "D");
229 curl
= ctx
->client
->ctx
;
231 curl_multi_setopt(curl
->handle
->multi
, CURLMOPT_SOCKETDATA
, NULL
);
232 curl_multi_setopt(curl
->handle
->multi
, CURLMOPT_SOCKETFUNCTION
, NULL
);
233 curl_multi_setopt(curl
->handle
->multi
, CURLMOPT_TIMERDATA
, NULL
);
234 curl_multi_setopt(curl
->handle
->multi
, CURLMOPT_TIMERFUNCTION
, NULL
);
236 php_http_object_method_dtor(&ctx
->timer
);
237 php_http_object_method_dtor(&ctx
->socket
);
238 php_http_object_method_dtor(&ctx
->once
);
239 php_http_object_method_dtor(&ctx
->wait
);
240 php_http_object_method_dtor(&ctx
->send
);
242 zend_string_release(ctx
->closure
.common
.function_name
);
243 zval_ptr_dtor(&ctx
->user
);
249 static php_http_client_curl_ops_t php_http_client_curl_user_ops
= {
250 &php_http_client_curl_user_init
,
251 &php_http_client_curl_user_dtor
,
252 &php_http_client_curl_user_once
,
253 &php_http_client_curl_user_wait
,
254 &php_http_client_curl_user_exec
,
257 php_http_client_curl_ops_t
*php_http_client_curl_user_ops_get()
259 return &php_http_client_curl_user_ops
;
262 static zend_class_entry
*php_http_client_curl_user_class_entry
;
264 zend_class_entry
*php_http_client_curl_user_get_class_entry()
266 return php_http_client_curl_user_class_entry
;
269 ZEND_BEGIN_ARG_INFO_EX(ai_init
, 0, 0, 1)
270 /* using IS_CALLABLE type hint would create a forwards compatibility break */
271 ZEND_ARG_INFO(0, run
)
273 ZEND_BEGIN_ARG_INFO_EX(ai_timer
, 0, 0, 1)
274 #if PHP_VERSION_ID >= 70000
275 ZEND_ARG_TYPE_INFO(0, timeout_ms
, IS_LONG
, 0)
277 ZEND_ARG_INFO(0, timeout_ms
)
280 ZEND_BEGIN_ARG_INFO_EX(ai_socket
, 0, 0, 2)
281 ZEND_ARG_INFO(0, socket
)
282 #if PHP_VERSION_ID >= 70000
283 ZEND_ARG_TYPE_INFO(0, action
, IS_LONG
, 0)
285 ZEND_ARG_INFO(0, action
)
288 ZEND_BEGIN_ARG_INFO_EX(ai_once
, 0, 0, 0)
290 ZEND_BEGIN_ARG_INFO_EX(ai_wait
, 0, 0, 0)
291 #if PHP_VERSION_ID >= 70000
292 ZEND_ARG_TYPE_INFO(0, timeout_ms
, IS_LONG
, 0)
294 ZEND_ARG_INFO(0, timeout_ms
)
297 ZEND_BEGIN_ARG_INFO_EX(ai_send
, 0, 0, 0)
300 static zend_function_entry php_http_client_curl_user_methods
[] = {
301 PHP_ABSTRACT_ME(HttpClientCurlUser
, init
, ai_init
)
302 PHP_ABSTRACT_ME(HttpClientCurlUser
, timer
, ai_timer
)
303 PHP_ABSTRACT_ME(HttpClientCurlUser
, socket
, ai_socket
)
304 PHP_ABSTRACT_ME(HttpClientCurlUser
, once
, ai_once
)
305 PHP_ABSTRACT_ME(HttpClientCurlUser
, wait
, ai_wait
)
306 PHP_ABSTRACT_ME(HttpClientCulrUser
, send
, ai_send
)
310 PHP_MINIT_FUNCTION(http_client_curl_user
)
312 zend_class_entry ce
= {0};
314 INIT_NS_CLASS_ENTRY(ce
, "http\\Client\\Curl", "User", php_http_client_curl_user_methods
);
315 php_http_client_curl_user_class_entry
= zend_register_internal_interface(&ce
);
317 zend_declare_class_constant_long(php_http_client_curl_user_class_entry
, ZEND_STRL("POLL_NONE"), CURL_POLL_NONE
);
318 zend_declare_class_constant_long(php_http_client_curl_user_class_entry
, ZEND_STRL("POLL_IN"), CURL_POLL_IN
);
319 zend_declare_class_constant_long(php_http_client_curl_user_class_entry
, ZEND_STRL("POLL_OUT"), CURL_POLL_OUT
);
320 zend_declare_class_constant_long(php_http_client_curl_user_class_entry
, ZEND_STRL("POLL_INOUT"), CURL_POLL_INOUT
);
321 zend_declare_class_constant_long(php_http_client_curl_user_class_entry
, ZEND_STRL("POLL_REMOVE"), CURL_POLL_REMOVE
);
326 #endif /* PHP_HTTP_HAVE_LIBCURL */
333 * vim600: noet sw=4 ts=4 fdm=marker
334 * vim<600: noet sw=4 ts=4