1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Sterling Hughes <sterling@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "php.h"
26
27 #if HAVE_CURL
28
29 #include "php_curl.h"
30
31 #include <curl/curl.h>
32 #include <curl/multi.h>
33
34 #ifdef HAVE_SYS_SELECT_H
35 #include <sys/select.h>
36 #endif
37
38 #ifdef HAVE_SYS_TIME_H
39 #include <sys/time.h>
40 #endif
41
42 #ifdef HAVE_SYS_TYPES_H
43 #include <sys/types.h>
44 #endif
45
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif
49
50 #define SAVE_CURLM_ERROR(__handle, __err) (__handle)->err.no = (int) __err;
51
52 /* {{{ proto resource curl_multi_init(void)
53 Returns a new cURL multi handle */
PHP_FUNCTION(curl_multi_init)54 PHP_FUNCTION(curl_multi_init)
55 {
56 php_curlm *mh;
57
58 if (zend_parse_parameters_none() == FAILURE) {
59 return;
60 }
61
62 mh = ecalloc(1, sizeof(php_curlm));
63 mh->multi = curl_multi_init();
64 mh->handlers = ecalloc(1, sizeof(php_curlm_handlers));
65
66 zend_llist_init(&mh->easyh, sizeof(zval), _php_curl_multi_cleanup_list, 0);
67
68 RETURN_RES(zend_register_resource(mh, le_curl_multi_handle));
69 }
70 /* }}} */
71
72 /* {{{ proto int curl_multi_add_handle(resource mh, resource ch)
73 Add a normal cURL handle to a cURL multi handle */
PHP_FUNCTION(curl_multi_add_handle)74 PHP_FUNCTION(curl_multi_add_handle)
75 {
76 zval *z_mh;
77 zval *z_ch;
78 php_curlm *mh;
79 php_curl *ch;
80 CURLMcode error = CURLM_OK;
81
82 ZEND_PARSE_PARAMETERS_START(2,2)
83 Z_PARAM_RESOURCE(z_mh)
84 Z_PARAM_RESOURCE(z_ch)
85 ZEND_PARSE_PARAMETERS_END();
86
87 if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
88 RETURN_FALSE;
89 }
90
91 if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(z_ch), le_curl_name, le_curl)) == NULL) {
92 RETURN_FALSE;
93 }
94
95 _php_curl_verify_handlers(ch, 1);
96
97 _php_curl_cleanup_handle(ch);
98
99 GC_ADDREF(Z_RES_P(z_ch));
100 zend_llist_add_element(&mh->easyh, z_ch);
101
102 error = curl_multi_add_handle(mh->multi, ch->cp);
103 SAVE_CURLM_ERROR(mh, error);
104
105 RETURN_LONG((zend_long) error);
106 }
107 /* }}} */
108
_php_curl_multi_cleanup_list(void * data)109 void _php_curl_multi_cleanup_list(void *data) /* {{{ */
110 {
111 zval *z_ch = (zval *)data;
112 php_curl *ch;
113
114 if (!z_ch) {
115 return;
116 }
117 if (!Z_RES_P(z_ch)->ptr) {
118 return;
119 }
120 if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(z_ch), le_curl_name, le_curl)) == NULL) {
121 return;
122 }
123
124 zend_list_delete(Z_RES_P(z_ch));
125 }
126 /* }}} */
127
128 /* Used internally as comparison routine passed to zend_list_del_element */
curl_compare_resources(zval * z1,zval * z2)129 static int curl_compare_resources( zval *z1, zval *z2 ) /* {{{ */
130 {
131 return (Z_TYPE_P(z1) == Z_TYPE_P(z2) &&
132 Z_TYPE_P(z1) == IS_RESOURCE &&
133 Z_RES_P(z1) == Z_RES_P(z2));
134 }
135 /* }}} */
136
137 /* Used to find the php_curl resource for a given curl easy handle */
_php_curl_multi_find_easy_handle(php_curlm * mh,CURL * easy)138 static zval *_php_curl_multi_find_easy_handle(php_curlm *mh, CURL *easy) /* {{{ */
139 {
140 php_curl *tmp_ch;
141 zend_llist_position pos;
142 zval *pz_ch_temp;
143
144 for(pz_ch_temp = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch_temp;
145 pz_ch_temp = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
146
147 if ((tmp_ch = (php_curl *)zend_fetch_resource(Z_RES_P(pz_ch_temp), le_curl_name, le_curl)) == NULL) {
148 return NULL;
149 }
150
151 if (tmp_ch->cp == easy) {
152 return pz_ch_temp;
153 }
154 }
155
156 return NULL;
157 }
158 /* }}} */
159
160 /* {{{ proto int curl_multi_remove_handle(resource mh, resource ch)
161 Remove a multi handle from a set of cURL handles */
PHP_FUNCTION(curl_multi_remove_handle)162 PHP_FUNCTION(curl_multi_remove_handle)
163 {
164 zval *z_mh;
165 zval *z_ch;
166 php_curlm *mh;
167 php_curl *ch;
168 CURLMcode error = CURLM_OK;
169
170 ZEND_PARSE_PARAMETERS_START(2,2)
171 Z_PARAM_RESOURCE(z_mh)
172 Z_PARAM_RESOURCE(z_ch)
173 ZEND_PARSE_PARAMETERS_END();
174
175 if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
176 RETURN_FALSE;
177 }
178
179 if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(z_ch), le_curl_name, le_curl)) == NULL) {
180 RETURN_FALSE;
181 }
182
183 error = curl_multi_remove_handle(mh->multi, ch->cp);
184 SAVE_CURLM_ERROR(mh, error);
185
186 RETVAL_LONG((zend_long) error);
187 zend_llist_del_element(&mh->easyh, z_ch, (int (*)(void *, void *))curl_compare_resources);
188
189 }
190 /* }}} */
191
192 #if LIBCURL_VERSION_NUM < 0x071c00
_make_timeval_struct(struct timeval * to,double timeout)193 static void _make_timeval_struct(struct timeval *to, double timeout) /* {{{ */
194 {
195 unsigned long conv;
196
197 conv = (unsigned long) (timeout * 1000000.0);
198 to->tv_sec = conv / 1000000;
199 to->tv_usec = conv % 1000000;
200 }
201 /* }}} */
202 #endif
203
204 /* {{{ proto int curl_multi_select(resource mh[, double timeout])
205 Get all the sockets associated with the cURL extension, which can then be "selected" */
PHP_FUNCTION(curl_multi_select)206 PHP_FUNCTION(curl_multi_select)
207 {
208 zval *z_mh;
209 php_curlm *mh;
210 double timeout = 1.0;
211 #if LIBCURL_VERSION_NUM >= 0x071c00 /* Available since 7.28.0 */
212 int numfds = 0;
213 #else
214 fd_set readfds;
215 fd_set writefds;
216 fd_set exceptfds;
217 int maxfd;
218 struct timeval to;
219 #endif
220 CURLMcode error = CURLM_OK;
221
222 ZEND_PARSE_PARAMETERS_START(1,2)
223 Z_PARAM_RESOURCE(z_mh)
224 Z_PARAM_OPTIONAL
225 Z_PARAM_DOUBLE(timeout)
226 ZEND_PARSE_PARAMETERS_END();
227
228 if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
229 RETURN_FALSE;
230 }
231
232 #if LIBCURL_VERSION_NUM >= 0x071c00 /* Available since 7.28.0 */
233 error = curl_multi_wait(mh->multi, NULL, 0, (unsigned long) (timeout * 1000.0), &numfds);
234 if (CURLM_OK != error) {
235 SAVE_CURLM_ERROR(mh, error);
236 RETURN_LONG(-1);
237 }
238
239 RETURN_LONG(numfds);
240 #else
241 _make_timeval_struct(&to, timeout);
242
243 FD_ZERO(&readfds);
244 FD_ZERO(&writefds);
245 FD_ZERO(&exceptfds);
246
247 error = curl_multi_fdset(mh->multi, &readfds, &writefds, &exceptfds, &maxfd);
248 SAVE_CURLM_ERROR(mh, error);
249
250 if (maxfd == -1) {
251 RETURN_LONG(-1);
252 }
253 RETURN_LONG(select(maxfd + 1, &readfds, &writefds, &exceptfds, &to));
254 #endif
255 }
256 /* }}} */
257
258 /* {{{ proto int curl_multi_exec(resource mh, int &still_running)
259 Run the sub-connections of the current cURL handle */
PHP_FUNCTION(curl_multi_exec)260 PHP_FUNCTION(curl_multi_exec)
261 {
262 zval *z_mh;
263 zval *z_still_running;
264 php_curlm *mh;
265 int still_running;
266 CURLMcode error = CURLM_OK;
267
268 ZEND_PARSE_PARAMETERS_START(2, 2)
269 Z_PARAM_RESOURCE(z_mh)
270 Z_PARAM_ZVAL(z_still_running)
271 ZEND_PARSE_PARAMETERS_END();
272
273 if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
274 RETURN_FALSE;
275 }
276
277 {
278 zend_llist_position pos;
279 php_curl *ch;
280 zval *pz_ch;
281
282 for (pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;
283 pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
284
285 if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(pz_ch), le_curl_name, le_curl)) == NULL) {
286 RETURN_FALSE;
287 }
288
289 _php_curl_verify_handlers(ch, 1);
290 }
291 }
292
293 still_running = zval_get_long(z_still_running);
294 error = curl_multi_perform(mh->multi, &still_running);
295 ZEND_TRY_ASSIGN_REF_LONG(z_still_running, still_running);
296
297 SAVE_CURLM_ERROR(mh, error);
298 RETURN_LONG((zend_long) error);
299 }
300 /* }}} */
301
302 /* {{{ proto string curl_multi_getcontent(resource ch)
303 Return the content of a cURL handle if CURLOPT_RETURNTRANSFER is set */
PHP_FUNCTION(curl_multi_getcontent)304 PHP_FUNCTION(curl_multi_getcontent)
305 {
306 zval *z_ch;
307 php_curl *ch;
308
309 ZEND_PARSE_PARAMETERS_START(1,1)
310 Z_PARAM_RESOURCE(z_ch)
311 ZEND_PARSE_PARAMETERS_END();
312
313 if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(z_ch), le_curl_name, le_curl)) == NULL) {
314 RETURN_FALSE;
315 }
316
317 if (ch->handlers->write->method == PHP_CURL_RETURN) {
318 if (!ch->handlers->write->buf.s) {
319 RETURN_EMPTY_STRING();
320 }
321 smart_str_0(&ch->handlers->write->buf);
322 RETURN_STR_COPY(ch->handlers->write->buf.s);
323 }
324
325 RETURN_NULL();
326 }
327 /* }}} */
328
329 /* {{{ proto array curl_multi_info_read(resource mh [, int &msgs_in_queue])
330 Get information about the current transfers */
PHP_FUNCTION(curl_multi_info_read)331 PHP_FUNCTION(curl_multi_info_read)
332 {
333 zval *z_mh;
334 php_curlm *mh;
335 CURLMsg *tmp_msg;
336 int queued_msgs;
337 zval *zmsgs_in_queue = NULL;
338 php_curl *ch;
339
340 ZEND_PARSE_PARAMETERS_START(1, 2)
341 Z_PARAM_RESOURCE(z_mh)
342 Z_PARAM_OPTIONAL
343 Z_PARAM_ZVAL(zmsgs_in_queue)
344 ZEND_PARSE_PARAMETERS_END();
345
346 if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
347 RETURN_FALSE;
348 }
349
350 tmp_msg = curl_multi_info_read(mh->multi, &queued_msgs);
351 if (tmp_msg == NULL) {
352 RETURN_FALSE;
353 }
354
355 if (zmsgs_in_queue) {
356 ZEND_TRY_ASSIGN_REF_LONG(zmsgs_in_queue, queued_msgs);
357 }
358
359 array_init(return_value);
360 add_assoc_long(return_value, "msg", tmp_msg->msg);
361 add_assoc_long(return_value, "result", tmp_msg->data.result);
362
363 /* find the original easy curl handle */
364 {
365 zval *pz_ch = _php_curl_multi_find_easy_handle(mh, tmp_msg->easy_handle);
366 if (pz_ch != NULL) {
367 /* we are adding a reference to the underlying php_curl
368 resource, so we need to add one to the resource's refcount
369 in order to ensure it doesn't get destroyed when the
370 underlying curl easy handle goes out of scope.
371 Normally you would call zval_copy_ctor( pz_ch ), or
372 SEPARATE_ZVAL, but those create new zvals, which is already
373 being done in add_assoc_resource */
374 Z_ADDREF_P(pz_ch);
375
376 /* we must save result to be able to read error message */
377 ch = (php_curl*)zend_fetch_resource(Z_RES_P(pz_ch), le_curl_name, le_curl);
378 SAVE_CURL_ERROR(ch, tmp_msg->data.result);
379
380 /* add_assoc_resource automatically creates a new zval to
381 wrap the "resource" represented by the current pz_ch */
382
383 add_assoc_zval(return_value, "handle", pz_ch);
384 }
385 }
386 }
387 /* }}} */
388
389 /* {{{ proto void curl_multi_close(resource mh)
390 Close a set of cURL handles */
PHP_FUNCTION(curl_multi_close)391 PHP_FUNCTION(curl_multi_close)
392 {
393 zval *z_mh;
394 php_curlm *mh;
395
396 ZEND_PARSE_PARAMETERS_START(1,1)
397 Z_PARAM_RESOURCE(z_mh)
398 ZEND_PARSE_PARAMETERS_END();
399
400 if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
401 RETURN_FALSE;
402 }
403
404 zend_list_close(Z_RES_P(z_mh));
405 }
406 /* }}} */
407
_php_curl_multi_close(zend_resource * rsrc)408 void _php_curl_multi_close(zend_resource *rsrc) /* {{{ */
409 {
410 php_curlm *mh = (php_curlm *)rsrc->ptr;
411 if (mh) {
412 zend_llist_position pos;
413 php_curl *ch;
414 zval *pz_ch;
415
416 for (pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;
417 pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
418 /* ptr is NULL means it already be freed */
419 if (Z_RES_P(pz_ch)->ptr) {
420 if ((ch = (php_curl *) zend_fetch_resource(Z_RES_P(pz_ch), le_curl_name, le_curl))) {
421 _php_curl_verify_handlers(ch, 0);
422 }
423 }
424 }
425
426 curl_multi_cleanup(mh->multi);
427 zend_llist_clean(&mh->easyh);
428 if (mh->handlers->server_push) {
429 zval_ptr_dtor(&mh->handlers->server_push->func_name);
430 efree(mh->handlers->server_push);
431 }
432 if (mh->handlers) {
433 efree(mh->handlers);
434 }
435 efree(mh);
436 rsrc->ptr = NULL;
437 }
438 }
439 /* }}} */
440
441 /* {{{ proto int curl_multi_errno(resource mh)
442 Return an integer containing the last multi curl error number */
PHP_FUNCTION(curl_multi_errno)443 PHP_FUNCTION(curl_multi_errno)
444 {
445 zval *z_mh;
446 php_curlm *mh;
447
448 ZEND_PARSE_PARAMETERS_START(1,1)
449 Z_PARAM_RESOURCE(z_mh)
450 ZEND_PARSE_PARAMETERS_END();
451
452 if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
453 RETURN_FALSE;
454 }
455
456 RETURN_LONG(mh->err.no);
457 }
458 /* }}} */
459
460 /* {{{ proto bool curl_multi_strerror(int code)
461 return string describing error code */
PHP_FUNCTION(curl_multi_strerror)462 PHP_FUNCTION(curl_multi_strerror)
463 {
464 zend_long code;
465 const char *str;
466
467 ZEND_PARSE_PARAMETERS_START(1,1)
468 Z_PARAM_LONG(code)
469 ZEND_PARSE_PARAMETERS_END();
470
471 str = curl_multi_strerror(code);
472 if (str) {
473 RETURN_STRING(str);
474 } else {
475 RETURN_NULL();
476 }
477 }
478 /* }}} */
479
480 #if LIBCURL_VERSION_NUM >= 0x072C00 /* Available since 7.44.0 */
481
_php_server_push_callback(CURL * parent_ch,CURL * easy,size_t num_headers,struct curl_pushheaders * push_headers,void * userp)482 static int _php_server_push_callback(CURL *parent_ch, CURL *easy, size_t num_headers, struct curl_pushheaders *push_headers, void *userp) /* {{{ */
483 {
484 php_curl *ch;
485 php_curl *parent;
486 php_curlm *mh = (php_curlm *)userp;
487 size_t rval = CURL_PUSH_DENY;
488 php_curlm_server_push *t = mh->handlers->server_push;
489 zval *pz_parent_ch = NULL;
490 zval pz_ch;
491 zval headers;
492 zval retval;
493 zend_resource *res;
494 char *header;
495 int error;
496 zend_fcall_info fci = empty_fcall_info;
497
498 pz_parent_ch = _php_curl_multi_find_easy_handle(mh, parent_ch);
499 if (pz_parent_ch == NULL) {
500 return rval;
501 }
502
503 parent = (php_curl*)zend_fetch_resource(Z_RES_P(pz_parent_ch), le_curl_name, le_curl);
504
505 ch = alloc_curl_handle();
506 ch->cp = easy;
507 _php_setup_easy_copy_handlers(ch, parent);
508
509 Z_ADDREF_P(pz_parent_ch);
510
511 res = zend_register_resource(ch, le_curl);
512 ch->res = res;
513 ZVAL_RES(&pz_ch, res);
514
515 size_t i;
516 array_init(&headers);
517 for(i=0; i<num_headers; i++) {
518 header = curl_pushheader_bynum(push_headers, i);
519 add_next_index_string(&headers, header);
520 }
521
522 zend_fcall_info_init(&t->func_name, 0, &fci, &t->fci_cache, NULL, NULL);
523
524 zend_fcall_info_argn(
525 &fci, 3,
526 pz_parent_ch,
527 &pz_ch,
528 &headers
529 );
530
531 fci.retval = &retval;
532
533 error = zend_call_function(&fci, &t->fci_cache);
534 zend_fcall_info_args_clear(&fci, 1);
535 zval_ptr_dtor_nogc(&headers);
536
537 if (error == FAILURE) {
538 php_error_docref(NULL, E_WARNING, "Cannot call the CURLMOPT_PUSHFUNCTION");
539 } else if (!Z_ISUNDEF(retval)) {
540 if (CURL_PUSH_DENY != zval_get_long(&retval)) {
541 rval = CURL_PUSH_OK;
542 GC_ADDREF(Z_RES(pz_ch));
543 zend_llist_add_element(&mh->easyh, &pz_ch);
544 } else {
545 /* libcurl will free this easy handle, avoid double free */
546 ch->cp = NULL;
547 }
548 }
549
550 return rval;
551 }
552 /* }}} */
553
554 #endif
555
_php_curl_multi_setopt(php_curlm * mh,zend_long option,zval * zvalue,zval * return_value)556 static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, zval *return_value) /* {{{ */
557 {
558 CURLMcode error = CURLM_OK;
559
560 switch (option) {
561 #if LIBCURL_VERSION_NUM >= 0x071000 /* 7.16.0 */
562 case CURLMOPT_PIPELINING:
563 #endif
564 #if LIBCURL_VERSION_NUM >= 0x071003 /* 7.16.3 */
565 case CURLMOPT_MAXCONNECTS:
566 #endif
567 #if LIBCURL_VERSION_NUM >= 0x071e00 /* 7.30.0 */
568 case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE:
569 case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE:
570 case CURLMOPT_MAX_HOST_CONNECTIONS:
571 case CURLMOPT_MAX_PIPELINE_LENGTH:
572 case CURLMOPT_MAX_TOTAL_CONNECTIONS:
573 #endif
574 {
575 zend_long lval = zval_get_long(zvalue);
576
577 if (option == CURLMOPT_PIPELINING && (lval & 1)) {
578 #if LIBCURL_VERSION_NUM >= 0x073e00 /* 7.62.0 */
579 php_error_docref(NULL, E_WARNING, "CURLPIPE_HTTP1 is no longer supported");
580 #else
581 php_error_docref(NULL, E_DEPRECATED, "CURLPIPE_HTTP1 is deprecated");
582 #endif
583 }
584 error = curl_multi_setopt(mh->multi, option, lval);
585 break;
586 }
587 #if LIBCURL_VERSION_NUM > 0x072D00 /* Available since 7.45.0 */
588 case CURLMOPT_PUSHFUNCTION:
589 if (mh->handlers->server_push == NULL) {
590 mh->handlers->server_push = ecalloc(1, sizeof(php_curlm_server_push));
591 } else if (!Z_ISUNDEF(mh->handlers->server_push->func_name)) {
592 zval_ptr_dtor(&mh->handlers->server_push->func_name);
593 mh->handlers->server_push->fci_cache = empty_fcall_info_cache;
594 }
595
596 ZVAL_COPY(&mh->handlers->server_push->func_name, zvalue);
597 mh->handlers->server_push->method = PHP_CURL_USER;
598 error = curl_multi_setopt(mh->multi, option, _php_server_push_callback);
599 if (error != CURLM_OK) {
600 return 0;
601 }
602 error = curl_multi_setopt(mh->multi, CURLMOPT_PUSHDATA, mh);
603 break;
604 #endif
605 default:
606 php_error_docref(NULL, E_WARNING, "Invalid curl multi configuration option");
607 error = CURLM_UNKNOWN_OPTION;
608 break;
609 }
610
611 SAVE_CURLM_ERROR(mh, error);
612
613 return error != CURLM_OK;
614 }
615 /* }}} */
616
617 /* {{{ proto int curl_multi_setopt(resource mh, int option, mixed value)
618 Set an option for the curl multi handle */
PHP_FUNCTION(curl_multi_setopt)619 PHP_FUNCTION(curl_multi_setopt)
620 {
621 zval *z_mh, *zvalue;
622 zend_long options;
623 php_curlm *mh;
624
625 ZEND_PARSE_PARAMETERS_START(3,3)
626 Z_PARAM_RESOURCE(z_mh)
627 Z_PARAM_LONG(options)
628 Z_PARAM_ZVAL(zvalue)
629 ZEND_PARSE_PARAMETERS_END();
630
631 if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
632 RETURN_FALSE;
633 }
634
635 if (!_php_curl_multi_setopt(mh, options, zvalue, return_value)) {
636 RETURN_TRUE;
637 } else {
638 RETURN_FALSE;
639 }
640 }
641 /* }}} */
642
643 #endif
644