xref: /PHP-7.4/ext/curl/multi.c (revision 0dda4a84)
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