add http\Client\AbstractClient#request(string method, string url[, array headers...
[m6w6/ext-http] / php_http_client.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: http |
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-2011, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #include "php_http_api.h"
14
15 #include <ext/spl/spl_observer.h>
16
17 PHP_HTTP_API php_http_client_t *php_http_client_init(php_http_client_t *h, php_http_client_ops_t *ops, php_http_resource_factory_t *rf, void *init_arg TSRMLS_DC)
18 {
19 php_http_client_t *free_h = NULL;
20
21 if (!h) {
22 free_h = h = emalloc(sizeof(*h));
23 }
24 memset(h, 0, sizeof(*h));
25
26 h->ops = ops;
27 if (rf) {
28 h->rf = rf;
29 } else if (ops->rsrc) {
30 h->rf = php_http_resource_factory_init(NULL, h->ops->rsrc, h, NULL);
31 }
32 h->buffer = php_http_buffer_init(NULL);
33 h->parser = php_http_message_parser_init(NULL TSRMLS_CC);
34 h->message = php_http_message_init(NULL, 0 TSRMLS_CC);
35
36 TSRMLS_SET_CTX(h->ts);
37
38 if (h->ops->init) {
39 if (!(h = h->ops->init(h, init_arg))) {
40 php_http_error(HE_WARNING, PHP_HTTP_E_CLIENT, "Could not initialize request");
41 if (free_h) {
42 h->ops->dtor = NULL;
43 php_http_client_free(&free_h);
44 }
45 }
46 }
47
48 return h;
49 }
50
51 PHP_HTTP_API void php_http_client_dtor(php_http_client_t *h)
52 {
53 if (h->ops->dtor) {
54 h->ops->dtor(h);
55 }
56
57 php_http_resource_factory_free(&h->rf);
58
59 php_http_message_parser_free(&h->parser);
60 php_http_message_free(&h->message);
61 php_http_buffer_free(&h->buffer);
62 }
63
64 PHP_HTTP_API void php_http_client_free(php_http_client_t **h)
65 {
66 if (*h) {
67 php_http_client_dtor(*h);
68 efree(*h);
69 *h = NULL;
70 }
71 }
72
73 PHP_HTTP_API php_http_client_t *php_http_client_copy(php_http_client_t *from, php_http_client_t *to)
74 {
75 if (!from->ops->copy) {
76 return NULL;
77 } else {
78 TSRMLS_FETCH_FROM_CTX(from->ts);
79
80 if (!to) {
81 to = ecalloc(1, sizeof(*to));
82 }
83
84 to->ops = from->ops;
85 if (from->rf) {
86 php_http_resource_factory_addref(from->rf);
87 to->rf = from->rf;
88 } else if (to->ops->rsrc){
89 to->rf = php_http_resource_factory_init(NULL, to->ops->rsrc, to, NULL);
90 }
91 to->buffer = php_http_buffer_init(NULL);
92 to->parser = php_http_message_parser_init(NULL TSRMLS_CC);
93 to->message = php_http_message_init(NULL, 0 TSRMLS_CC);
94
95 TSRMLS_SET_CTX(to->ts);
96
97 return to->ops->copy(from, to);
98 }
99 }
100
101 PHP_HTTP_API STATUS php_http_client_exec(php_http_client_t *h, php_http_message_t *msg)
102 {
103 if (h->ops->exec) {
104 return h->ops->exec(h, msg);
105 }
106 return FAILURE;
107 }
108
109 PHP_HTTP_API STATUS php_http_client_reset(php_http_client_t *h)
110 {
111 if (h->ops->reset) {
112 return h->ops->reset(h);
113 }
114 return FAILURE;
115 }
116
117 PHP_HTTP_API STATUS php_http_client_setopt(php_http_client_t *h, php_http_client_setopt_opt_t opt, void *arg)
118 {
119 if (h->ops->setopt) {
120 return h->ops->setopt(h, opt, arg);
121 }
122 return FAILURE;
123 }
124
125 PHP_HTTP_API STATUS php_http_client_getopt(php_http_client_t *h, php_http_client_getopt_opt_t opt, void *arg)
126 {
127 if (h->ops->getopt) {
128 return h->ops->getopt(h, opt, arg);
129 }
130 return FAILURE;
131 }
132
133 #define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpClient, method, 0, req_args)
134 #define PHP_HTTP_EMPTY_ARGS(method) PHP_HTTP_EMPTY_ARGS_EX(HttpClient, method, 0)
135 #define PHP_HTTP_CLIENT_ME(method, visibility) PHP_ME(HttpClient, method, PHP_HTTP_ARGS(HttpClient, method), visibility)
136 #define PHP_HTTP_CLIENT_ALIAS(method, func) PHP_HTTP_STATIC_ME_ALIAS(method, func, PHP_HTTP_ARGS(HttpClient, method))
137 #define PHP_HTTP_CLIENT_MALIAS(me, al, vis) ZEND_FENTRY(me, ZEND_MN(HttpClient_##al), PHP_HTTP_ARGS(HttpClient, al), vis)
138
139 PHP_HTTP_BEGIN_ARGS(__construct, 0)
140 PHP_HTTP_ARG_ARR(options, 1, 0)
141 PHP_HTTP_END_ARGS;
142
143 PHP_HTTP_EMPTY_ARGS(getOptions);
144 PHP_HTTP_BEGIN_ARGS(setOptions, 0)
145 PHP_HTTP_ARG_ARR(options, 1, 0)
146 PHP_HTTP_END_ARGS;
147
148 PHP_HTTP_EMPTY_ARGS(getSslOptions);
149 PHP_HTTP_BEGIN_ARGS(setSslOptions, 0)
150 PHP_HTTP_ARG_ARR(ssl_options, 1, 0)
151 PHP_HTTP_END_ARGS;
152
153 PHP_HTTP_BEGIN_ARGS(addSslOptions, 0)
154 PHP_HTTP_ARG_ARR(ssl_options, 1, 0)
155 PHP_HTTP_END_ARGS;
156
157 PHP_HTTP_EMPTY_ARGS(getCookies);
158 PHP_HTTP_BEGIN_ARGS(setCookies, 0)
159 PHP_HTTP_ARG_VAL(cookies, 0)
160 PHP_HTTP_END_ARGS;
161
162 PHP_HTTP_BEGIN_ARGS(addCookies, 1)
163 PHP_HTTP_ARG_VAL(cookies, 0)
164 PHP_HTTP_END_ARGS;
165
166 PHP_HTTP_EMPTY_ARGS(enableCookies);
167 PHP_HTTP_BEGIN_ARGS(resetCookies, 0)
168 PHP_HTTP_ARG_VAL(session_only, 0)
169 PHP_HTTP_END_ARGS;
170 PHP_HTTP_EMPTY_ARGS(flushCookies);
171
172 PHP_HTTP_EMPTY_ARGS(getResponseMessageClass);
173 PHP_HTTP_BEGIN_ARGS(setResponseMessageClass, 1)
174 PHP_HTTP_ARG_VAL(message_class_name, 0)
175 PHP_HTTP_END_ARGS;
176
177 PHP_HTTP_EMPTY_ARGS(getResponseMessage);
178 PHP_HTTP_EMPTY_ARGS(getRequestMessage);
179 PHP_HTTP_EMPTY_ARGS(getHistory);
180 PHP_HTTP_EMPTY_ARGS(clearHistory);
181
182 PHP_HTTP_BEGIN_ARGS(setRequest, 1)
183 PHP_HTTP_ARG_OBJ(http\\Client\\Request, request, 1)
184 PHP_HTTP_END_ARGS;
185 PHP_HTTP_EMPTY_ARGS(getRequest);
186
187 PHP_HTTP_EMPTY_ARGS(getObservers);
188 PHP_HTTP_BEGIN_ARGS(attach, 1)
189 PHP_HTTP_ARG_OBJ(SplObserver, observer, 0)
190 PHP_HTTP_END_ARGS;
191 PHP_HTTP_BEGIN_ARGS(detach, 1)
192 PHP_HTTP_ARG_OBJ(SplObserver, observer, 0)
193 PHP_HTTP_END_ARGS;
194 PHP_HTTP_EMPTY_ARGS(notify);
195 PHP_HTTP_EMPTY_ARGS(getProgress);
196 PHP_HTTP_BEGIN_ARGS(getTransferInfo, 0)
197 PHP_HTTP_ARG_VAL(name, 0)
198 PHP_HTTP_END_ARGS;
199
200 PHP_HTTP_BEGIN_ARGS(request, 2)
201 PHP_HTTP_ARG_VAL(method, 0)
202 PHP_HTTP_ARG_VAL(url, 0)
203 PHP_HTTP_ARG_ARR(headers, 1, 0)
204 PHP_HTTP_ARG_VAL(body, 0)
205 PHP_HTTP_ARG_ARR(options, 1, 0)
206 PHP_HTTP_END_ARGS;
207
208 static zend_class_entry *php_http_client_class_entry;
209
210 zend_class_entry *php_http_client_get_class_entry(void)
211 {
212 return php_http_client_class_entry;
213 }
214
215 static zend_function_entry php_http_client_method_entry[] = {
216 PHP_HTTP_CLIENT_ME(__construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
217 PHP_HTTP_CLIENT_ME(getObservers, ZEND_ACC_PUBLIC)
218 PHP_HTTP_CLIENT_ME(notify, ZEND_ACC_PUBLIC)
219 PHP_HTTP_CLIENT_ME(attach, ZEND_ACC_PUBLIC)
220 PHP_HTTP_CLIENT_ME(detach, ZEND_ACC_PUBLIC)
221 PHP_HTTP_CLIENT_ME(getProgress, ZEND_ACC_PUBLIC)
222 PHP_HTTP_CLIENT_ME(getTransferInfo, ZEND_ACC_PUBLIC)
223
224 PHP_HTTP_CLIENT_ME(setOptions, ZEND_ACC_PUBLIC)
225 PHP_HTTP_CLIENT_ME(getOptions, ZEND_ACC_PUBLIC)
226 PHP_HTTP_CLIENT_ME(setSslOptions, ZEND_ACC_PUBLIC)
227 PHP_HTTP_CLIENT_ME(getSslOptions, ZEND_ACC_PUBLIC)
228 PHP_HTTP_CLIENT_ME(addSslOptions, ZEND_ACC_PUBLIC)
229
230 PHP_HTTP_CLIENT_ME(addCookies, ZEND_ACC_PUBLIC)
231 PHP_HTTP_CLIENT_ME(getCookies, ZEND_ACC_PUBLIC)
232 PHP_HTTP_CLIENT_ME(setCookies, ZEND_ACC_PUBLIC)
233
234 PHP_HTTP_CLIENT_ME(enableCookies, ZEND_ACC_PUBLIC)
235 PHP_HTTP_CLIENT_ME(resetCookies, ZEND_ACC_PUBLIC)
236 PHP_HTTP_CLIENT_ME(flushCookies, ZEND_ACC_PUBLIC)
237
238 PHP_HTTP_CLIENT_ME(setRequest, ZEND_ACC_PUBLIC)
239 PHP_HTTP_CLIENT_ME(getRequest, ZEND_ACC_PUBLIC)
240
241 PHP_HTTP_CLIENT_ME(getResponseMessage, ZEND_ACC_PUBLIC)
242 PHP_HTTP_CLIENT_ME(getRequestMessage, ZEND_ACC_PUBLIC)
243 PHP_HTTP_CLIENT_ME(getHistory, ZEND_ACC_PUBLIC)
244 PHP_HTTP_CLIENT_ME(clearHistory, ZEND_ACC_PUBLIC)
245
246 PHP_HTTP_CLIENT_ME(getResponseMessageClass, ZEND_ACC_PUBLIC)
247 PHP_HTTP_CLIENT_ME(setResponseMessageClass, ZEND_ACC_PUBLIC)
248
249 PHP_HTTP_CLIENT_ME(request, ZEND_ACC_PUBLIC)
250
251 EMPTY_FUNCTION_ENTRY
252 };
253
254 static zend_object_handlers php_http_client_object_handlers;
255
256 zend_object_handlers *php_http_client_get_object_handlers(void)
257 {
258 return &php_http_client_object_handlers;
259 }
260
261 static php_http_client_ops_t php_http_client_user_ops = {
262 NULL,
263 NULL,
264 NULL,
265 NULL,
266 NULL,
267 NULL,
268 NULL,
269 NULL,
270 (php_http_new_t) php_http_client_object_new_ex,
271 php_http_client_get_class_entry
272 };
273
274 zend_object_value php_http_client_object_new(zend_class_entry *ce TSRMLS_DC)
275 {
276 return php_http_client_object_new_ex(ce, NULL, NULL TSRMLS_CC);
277 }
278
279 zend_object_value php_http_client_object_new_ex(zend_class_entry *ce, php_http_client_t *r, php_http_client_object_t **ptr TSRMLS_DC)
280 {
281 zend_object_value ov;
282 php_http_client_object_t *o;
283
284 o = ecalloc(1, sizeof(*o));
285 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
286 object_properties_init((zend_object *) o, ce);
287
288 ov.handle = zend_objects_store_put(o, NULL, php_http_client_object_free, NULL TSRMLS_CC);
289 ov.handlers = &php_http_client_object_handlers;
290
291 if (!(o->client = r)) {
292 o->client = php_http_client_init(NULL, &php_http_client_user_ops, NULL, &ov TSRMLS_CC);
293 }
294
295 if (ptr) {
296 *ptr = o;
297 }
298
299 return ov;
300 }
301
302 zend_object_value php_http_client_object_clone(zval *this_ptr TSRMLS_DC)
303 {
304 zend_object_value new_ov;
305 php_http_client_object_t *new_obj, *old_obj = zend_object_store_get_object(this_ptr TSRMLS_CC);
306
307 new_ov = php_http_client_object_new_ex(old_obj->zo.ce, php_http_client_copy(old_obj->client, NULL), &new_obj TSRMLS_CC);
308 zend_objects_clone_members(&new_obj->zo, new_ov, &old_obj->zo, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC);
309
310 return new_ov;
311 }
312
313 void php_http_client_object_free(void *object TSRMLS_DC)
314 {
315 php_http_client_object_t *o = (php_http_client_object_t *) object;
316
317 php_http_client_free(&o->client);
318 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
319 efree(o);
320 }
321
322 static inline zend_object_value php_http_client_object_message(zval *this_ptr, php_http_message_t *msg TSRMLS_DC)
323 {
324 zend_object_value ov;
325 zval *zcn = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("responseMessageClass"), 0 TSRMLS_CC);
326 zend_class_entry *class_entry;
327
328 if (Z_STRLEN_P(zcn)
329 && (class_entry = zend_fetch_class(Z_STRVAL_P(zcn), Z_STRLEN_P(zcn), 0 TSRMLS_CC))
330 && (SUCCESS == php_http_new(&ov, class_entry, (php_http_new_t) php_http_message_object_new_ex, php_http_client_response_get_class_entry(), msg, NULL TSRMLS_CC))) {
331 return ov;
332 } else {
333 return php_http_message_object_new_ex(php_http_client_response_get_class_entry(), msg, NULL TSRMLS_CC);
334 }
335 }
336
337 STATUS php_http_client_object_handle_request(zval *zclient, zval **zreq TSRMLS_DC)
338 {
339 php_http_client_object_t *obj = zend_object_store_get_object(zclient TSRMLS_CC);
340 php_http_client_progress_t *progress;
341 zval *zoptions;
342
343 /* do we have a valid request? */
344 if (*zreq) {
345 /* remember the request */
346 zend_update_property(php_http_client_class_entry, zclient, ZEND_STRL("request"), *zreq TSRMLS_CC);
347 } else {
348 /* maybe a request is already set */
349 *zreq = zend_read_property(php_http_client_class_entry, zclient, ZEND_STRL("request"), 0 TSRMLS_CC);
350
351 if (Z_TYPE_PP(zreq) != IS_OBJECT || !instanceof_function(Z_OBJCE_PP(zreq), php_http_client_request_get_class_entry() TSRMLS_CC)) {
352 php_http_error(HE_WARNING, PHP_HTTP_E_CLIENT, "The client does not have a valid request set");
353 return FAILURE;
354 }
355 }
356
357 /* reset request handle */
358 php_http_client_reset(obj->client);
359
360 /* reset transfer info */
361 zend_update_property_null(php_http_client_class_entry, zclient, ZEND_STRL("info") TSRMLS_CC);
362
363 /* set client options */
364 zoptions = zend_read_property(php_http_client_class_entry, zclient, ZEND_STRL("options"), 0 TSRMLS_CC);
365 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_SETTINGS, Z_ARRVAL_P(zoptions));
366 /* set request options */
367 zoptions = zend_read_property(php_http_client_request_get_class_entry(), *zreq, ZEND_STRL("options"), 0 TSRMLS_CC);
368 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_SETTINGS, Z_ARRVAL_P(zoptions));
369
370 /* set progress callback */
371 if (SUCCESS == php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, &progress)) {
372 if (!progress->callback) {
373 php_http_client_progress_callback_t *callback = emalloc(sizeof(*callback));
374
375 callback->type = PHP_HTTP_CLIENT_PROGRESS_CALLBACK_USER;
376 callback->pass_state = 0;
377 MAKE_STD_ZVAL(callback->func.user);
378 array_init(callback->func.user);
379 Z_ADDREF_P(zclient);
380 add_next_index_zval(callback->func.user, zclient);
381 add_next_index_stringl(callback->func.user, ZEND_STRL("notify"), 1);
382
383 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_PROGRESS_CALLBACK, callback);
384 }
385 progress->state.info = "start";
386 php_http_client_progress_notify(progress TSRMLS_CC);
387 progress->state.started = 1;
388 }
389
390 return SUCCESS;
391 }
392
393
394 STATUS php_http_client_object_handle_response(zval *zclient TSRMLS_DC)
395 {
396 php_http_client_object_t *obj = zend_object_store_get_object(zclient TSRMLS_CC);
397 php_http_client_progress_t *progress;
398 php_http_message_t *msg;
399 zval *info;
400
401 /* always fetch info */
402 MAKE_STD_ZVAL(info);
403 array_init(info);
404 php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_TRANSFER_INFO, Z_ARRVAL_P(info));
405 zend_update_property(php_http_client_class_entry, zclient, ZEND_STRL("transferInfo"), info TSRMLS_CC);
406 zval_ptr_dtor(&info);
407
408 if ((msg = obj->client->message)) {
409 /* update history */
410 if (i_zend_is_true(zend_read_property(php_http_client_class_entry, zclient, ZEND_STRL("recordHistory"), 0 TSRMLS_CC))) {
411 zval *new_hist, *old_hist = zend_read_property(php_http_client_class_entry, zclient, ZEND_STRL("history"), 0 TSRMLS_CC);
412 zend_object_value ov = php_http_client_object_message(zclient, php_http_message_copy(msg, NULL) TSRMLS_CC);
413
414 MAKE_STD_ZVAL(new_hist);
415 ZVAL_OBJVAL(new_hist, ov, 0);
416
417 if (Z_TYPE_P(old_hist) == IS_OBJECT) {
418 php_http_message_object_prepend(new_hist, old_hist, 1 TSRMLS_CC);
419 }
420
421 zend_update_property(php_http_client_class_entry, zclient, ZEND_STRL("history"), new_hist TSRMLS_CC);
422 zval_ptr_dtor(&new_hist);
423 }
424
425 /* update response info */
426 if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, msg)) {
427 zval *message;
428
429 MAKE_STD_ZVAL(message);
430 ZVAL_OBJVAL(message, php_http_client_object_message(zclient, msg TSRMLS_CC), 0);
431 zend_update_property(php_http_client_class_entry, zclient, ZEND_STRL("responseMessage"), message TSRMLS_CC);
432 zval_ptr_dtor(&message);
433
434 obj->client->message = php_http_message_init(NULL, 0 TSRMLS_CC);
435 msg = msg->parent;
436 } else {
437 zend_update_property_null(php_http_client_class_entry, zclient, ZEND_STRL("responseMessage") TSRMLS_CC);
438 }
439 } else {
440 zend_update_property_null(php_http_client_class_entry, zclient, ZEND_STRL("responseMessage") TSRMLS_CC);
441 }
442
443 /* there might be a 100-Continue response in between */
444 while (msg && !PHP_HTTP_MESSAGE_TYPE(REQUEST, msg)) {
445 msg = msg->parent;
446 }
447
448 if (PHP_HTTP_MESSAGE_TYPE(REQUEST, msg)) {
449 zval *message;
450
451 /* update the actual request message */
452 MAKE_STD_ZVAL(message);
453 ZVAL_OBJVAL(message, php_http_message_object_new_ex(php_http_message_get_class_entry(), php_http_message_copy_ex(msg, NULL, 0), NULL TSRMLS_CC), 0);
454 zend_update_property(php_http_client_class_entry, zclient, ZEND_STRL("requestMessage"), message TSRMLS_CC);
455 zval_ptr_dtor(&message);
456 }
457
458 if (SUCCESS == php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, &progress)) {
459 progress->state.info = "finished";
460 progress->state.finished = 1;
461 php_http_client_progress_notify(progress TSRMLS_CC);
462 }
463 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_PROGRESS_CALLBACK, NULL);
464
465 return SUCCESS;
466 }
467
468 void php_http_client_options_set(zval *this_ptr, zval *opts TSRMLS_DC)
469 {
470 php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
471 HashPosition pos;
472 zval *new_opts;
473 zend_class_entry *this_ce = Z_OBJCE_P(getThis());
474 zend_bool is_client = instanceof_function(this_ce, php_http_client_class_entry TSRMLS_CC);
475
476 MAKE_STD_ZVAL(new_opts);
477 array_init(new_opts);
478
479 if (!opts || !zend_hash_num_elements(Z_ARRVAL_P(opts))) {
480 zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC);
481 zval_ptr_dtor(&new_opts);
482 } else {
483 zval *old_opts, *add_opts, **opt;
484
485 MAKE_STD_ZVAL(add_opts);
486 array_init(add_opts);
487 /* some options need extra attention -- thus cannot use array_merge() directly */
488 FOREACH_KEYVAL(pos, opts, key, opt) {
489 if (key.type == HASH_KEY_IS_STRING) {
490 #define KEYMATCH(k, s) ((sizeof(s)==k.len) && !strcasecmp(k.str, s))
491 if (Z_TYPE_PP(opt) == IS_ARRAY && (KEYMATCH(key, "ssl") || KEYMATCH(key, "cookies"))) {
492 php_http_client_options_set_subr(getThis(), key.str, key.len, *opt, 0 TSRMLS_CC);
493 } else if (is_client && (KEYMATCH(key, "recordHistory") || KEYMATCH(key, "responseMessageClass"))) {
494 zend_update_property(this_ce, getThis(), key.str, key.len-1, *opt TSRMLS_CC);
495 } else if (Z_TYPE_PP(opt) == IS_NULL) {
496 old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
497 if (Z_TYPE_P(old_opts) == IS_ARRAY) {
498 zend_symtable_del(Z_ARRVAL_P(old_opts), key.str, key.len);
499 }
500 } else {
501 Z_ADDREF_P(*opt);
502 add_assoc_zval_ex(add_opts, key.str, key.len, *opt);
503 }
504 }
505 }
506
507 old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
508 if (Z_TYPE_P(old_opts) == IS_ARRAY) {
509 array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL_P(new_opts));
510 }
511 array_join(Z_ARRVAL_P(add_opts), Z_ARRVAL_P(new_opts), 0, 0);
512 zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC);
513 zval_ptr_dtor(&new_opts);
514 zval_ptr_dtor(&add_opts);
515 }
516 }
517
518 void php_http_client_options_set_subr(zval *this_ptr, char *key, size_t len, zval *opts, int overwrite TSRMLS_DC)
519 {
520 if (overwrite || (opts && zend_hash_num_elements(Z_ARRVAL_P(opts)))) {
521 zend_class_entry *this_ce = Z_OBJCE_P(getThis());
522 zval *old_opts, *new_opts, **entry = NULL;
523
524 MAKE_STD_ZVAL(new_opts);
525 array_init(new_opts);
526 old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
527 if (Z_TYPE_P(old_opts) == IS_ARRAY) {
528 array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL_P(new_opts));
529 }
530
531 if (overwrite) {
532 if (opts && zend_hash_num_elements(Z_ARRVAL_P(opts))) {
533 Z_ADDREF_P(opts);
534 zend_symtable_update(Z_ARRVAL_P(new_opts), key, len, (void *) &opts, sizeof(zval *), NULL);
535 } else {
536 zend_symtable_del(Z_ARRVAL_P(new_opts), key, len);
537 }
538 } else if (opts && zend_hash_num_elements(Z_ARRVAL_P(opts))) {
539 if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(new_opts), key, len, (void *) &entry)) {
540 array_join(Z_ARRVAL_P(opts), Z_ARRVAL_PP(entry), 0, 0);
541 } else {
542 Z_ADDREF_P(opts);
543 zend_symtable_update(Z_ARRVAL_P(new_opts), key, len, (void *) &opts, sizeof(zval *), NULL);
544 }
545 }
546
547 zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC);
548 zval_ptr_dtor(&new_opts);
549 }
550 }
551
552 void php_http_client_options_get_subr(zval *this_ptr, char *key, size_t len, zval *return_value TSRMLS_DC)
553 {
554 zend_class_entry *this_ce = Z_OBJCE_P(getThis());
555 zval **options, *opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
556
557 if ((Z_TYPE_P(opts) == IS_ARRAY) && (SUCCESS == zend_symtable_find(Z_ARRVAL_P(opts), key, len, (void *) &options))) {
558 RETVAL_ZVAL(*options, 1, 0);
559 }
560 }
561
562 PHP_METHOD(HttpClient, __construct)
563 {
564 with_error_handling(EH_THROW, php_http_exception_get_class_entry()) {
565 zval *os, *opts = NULL;
566
567 MAKE_STD_ZVAL(os);
568 object_init_ex(os, spl_ce_SplObjectStorage);
569 zend_update_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), os TSRMLS_CC);
570 zval_ptr_dtor(&os);
571
572 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts)) {
573 php_http_client_options_set(getThis(), opts TSRMLS_CC);
574 }
575
576 } end_error_handling();
577 }
578
579 PHP_METHOD(HttpClient, getObservers)
580 {
581 with_error_handling(EH_THROW, php_http_exception_get_class_entry()) {
582 if (SUCCESS == zend_parse_parameters_none()) {
583 RETVAL_PROP(php_http_client_class_entry, "observers");
584 }
585 } end_error_handling();
586 }
587
588 static int notify(zend_object_iterator *iter, void *puser TSRMLS_DC)
589 {
590 zval **observer = NULL;
591
592 iter->funcs->get_current_data(iter, &observer TSRMLS_CC);
593 if (observer) {
594 zval *retval;
595
596 zend_call_method_with_1_params(observer, NULL, NULL, "update", &retval, puser);
597 zval_ptr_dtor(&retval);
598 return SUCCESS;
599 }
600 return FAILURE;
601 }
602
603 PHP_METHOD(HttpClient, notify)
604 {
605 if (SUCCESS == zend_parse_parameters_none()) {
606 zval *observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
607
608 if (Z_TYPE_P(observers) == IS_OBJECT) {
609 Z_ADDREF_P(getThis());
610 spl_iterator_apply(observers, notify, getThis() TSRMLS_CC);
611 zval_ptr_dtor(&getThis());
612 }
613 }
614
615 RETVAL_ZVAL(getThis(), 1, 0);
616 }
617
618 PHP_METHOD(HttpClient, attach)
619 {
620 zval *observer;
621
622 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &observer, spl_ce_SplObserver)) {
623 zval *retval, *observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
624 zend_call_method_with_1_params(&observers, NULL, NULL, "attach", &retval, observer);
625 zval_ptr_dtor(&retval);
626 }
627
628 RETVAL_ZVAL(getThis(), 1, 0);
629 }
630
631 PHP_METHOD(HttpClient, detach)
632 {
633 zval *observer;
634
635 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &observer, spl_ce_SplObserver)) {
636 zval *retval, *observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
637 zend_call_method_with_1_params(&observers, NULL, NULL, "detach", &retval, observer);
638 zval_ptr_dtor(&retval);
639 }
640
641 RETVAL_ZVAL(getThis(), 1, 0);
642 }
643
644 PHP_METHOD(HttpClient, getProgress)
645 {
646 if (SUCCESS == zend_parse_parameters_none()) {
647 php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
648 php_http_client_progress_t *progress = NULL;
649
650 if (SUCCESS == php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, &progress)) {
651 object_init(return_value);
652 add_property_bool(return_value, "started", progress->state.started);
653 add_property_bool(return_value, "finished", progress->state.finished);
654 add_property_string(return_value, "info", STR_PTR(progress->state.info), 1);
655 add_property_double(return_value, "dltotal", progress->state.dl.total);
656 add_property_double(return_value, "dlnow", progress->state.dl.now);
657 add_property_double(return_value, "ultotal", progress->state.ul.total);
658 add_property_double(return_value, "ulnow", progress->state.ul.now);
659 }
660 }
661 }
662
663 PHP_METHOD(HttpClient, getTransferInfo)
664 {
665 char *info_name = NULL;
666 int info_len = 0;
667
668 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &info_name, &info_len)) {
669 zval **infop, *temp = NULL, *info = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("transferInfo"), 0 TSRMLS_CC);
670
671 /* request completed? */
672 if (Z_TYPE_P(info) != IS_ARRAY) {
673 php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
674
675 MAKE_STD_ZVAL(temp);
676 array_init(temp);
677 php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_TRANSFER_INFO, Z_ARRVAL_P(temp));
678 info = temp;
679 }
680
681 if (info_len && info_name) {
682 if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(info), php_http_pretty_key(info_name, info_len, 0, 0), info_len + 1, (void *) &infop)) {
683 RETVAL_ZVAL(*infop, 1, 0);
684 } else {
685 php_http_error(HE_NOTICE, PHP_HTTP_E_INVALID_PARAM, "Could not find transfer info named %s", info_name);
686 RETVAL_FALSE;
687 }
688 } else {
689 RETVAL_ZVAL(info, 1, 0);
690 }
691
692 if (temp) {
693 zval_ptr_dtor(&temp);
694 }
695 return;
696 }
697 RETURN_FALSE;
698 }
699
700 PHP_METHOD(HttpClient, setOptions)
701 {
702 zval *opts = NULL;
703
704 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts)) {
705 php_http_client_options_set(getThis(), opts TSRMLS_CC);
706
707 RETVAL_ZVAL(getThis(), 1, 0);
708 }
709 }
710
711 PHP_METHOD(HttpClient, getOptions)
712 {
713 if (SUCCESS == zend_parse_parameters_none()) {
714 RETURN_PROP(php_http_client_class_entry, "options");
715 }
716 RETURN_FALSE;
717 }
718
719 PHP_METHOD(HttpClient, setSslOptions)
720 {
721 zval *opts = NULL;
722
723 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts)) {
724 php_http_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 1 TSRMLS_CC);
725
726 RETVAL_ZVAL(getThis(), 1, 0);
727 }
728 }
729
730 PHP_METHOD(HttpClient, addSslOptions)
731 {
732 zval *opts = NULL;
733
734 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts)) {
735 php_http_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 0 TSRMLS_CC);
736
737 RETVAL_ZVAL(getThis(), 1, 0);
738 }
739 }
740
741 PHP_METHOD(HttpClient, getSslOptions)
742 {
743 if (SUCCESS == zend_parse_parameters_none()) {
744 php_http_client_options_get_subr(getThis(), ZEND_STRS("ssl"), return_value TSRMLS_CC);
745 }
746 }
747
748 PHP_METHOD(HttpClient, setCookies)
749 {
750 zval *opts = NULL;
751
752 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts)) {
753 php_http_client_options_set_subr(getThis(), ZEND_STRS("cookies"), opts, 1 TSRMLS_CC);
754
755 RETVAL_ZVAL(getThis(), 1, 0);
756 }
757 }
758
759 PHP_METHOD(HttpClient, addCookies)
760 {
761 zval *opts = NULL;
762
763 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts)) {
764 php_http_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 0 TSRMLS_CC);
765
766 RETVAL_ZVAL(getThis(), 1, 0);
767 }
768 }
769
770 PHP_METHOD(HttpClient, getCookies)
771 {
772 if (SUCCESS == zend_parse_parameters_none()) {
773 php_http_client_options_get_subr(getThis(), ZEND_STRS("cookies"), return_value TSRMLS_CC);
774 }
775 }
776
777 PHP_METHOD(HttpClient, enableCookies)
778 {
779 if (SUCCESS == zend_parse_parameters_none()){
780 php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
781
782 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_COOKIES_ENABLE, NULL);
783 }
784 RETVAL_ZVAL(getThis(), 1, 0);
785 }
786
787 PHP_METHOD(HttpClient, resetCookies)
788 {
789 zend_bool session_only = 0;
790
791 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &session_only)) {
792 php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
793 if (session_only) {
794 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_COOKIES_RESET_SESSION, NULL);
795 } else {
796 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_COOKIES_RESET, NULL);
797 }
798 }
799 RETVAL_ZVAL(getThis(), 1, 0);
800 }
801
802 PHP_METHOD(HttpClient, flushCookies)
803 {
804 if (SUCCESS == zend_parse_parameters_none()) {
805 php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
806
807 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_COOKIES_FLUSH, NULL);
808 }
809 RETVAL_ZVAL(getThis(), 1, 0);
810 }
811
812 PHP_METHOD(HttpClient, getResponseMessage)
813 {
814 with_error_handling(EH_THROW, php_http_exception_get_class_entry()) {
815 if (SUCCESS == zend_parse_parameters_none()) {
816 zval *message = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("responseMessage"), 0 TSRMLS_CC);
817
818 if (Z_TYPE_P(message) == IS_OBJECT) {
819 RETVAL_OBJECT(message, 1);
820 } else {
821 php_http_error(HE_WARNING, PHP_HTTP_E_RUNTIME, "HttpClient does not contain a response message");
822 }
823 }
824 } end_error_handling();
825 }
826
827 PHP_METHOD(HttpClient, getRequestMessage)
828 {
829 with_error_handling(EH_THROW, php_http_exception_get_class_entry()) {
830 if (SUCCESS == zend_parse_parameters_none()) {
831 zval *message = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("requestMessage"), 0 TSRMLS_CC);
832
833 if (Z_TYPE_P(message) == IS_OBJECT) {
834 RETVAL_OBJECT(message, 1);
835 } else {
836 php_http_error(HE_WARNING, PHP_HTTP_E_RUNTIME, "HttpClient does not contain a request message");
837 }
838 }
839 } end_error_handling();
840 }
841
842 PHP_METHOD(HttpClient, getHistory)
843 {
844 with_error_handling(EH_THROW, php_http_exception_get_class_entry()) {
845 if (SUCCESS == zend_parse_parameters_none()) {
846 zval *hist = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("history"), 0 TSRMLS_CC);
847
848 if (Z_TYPE_P(hist) == IS_OBJECT) {
849 RETVAL_OBJECT(hist, 1);
850 } else {
851 php_http_error(HE_WARNING, PHP_HTTP_E_RUNTIME, "The history is empty");
852 }
853 }
854 } end_error_handling();
855 }
856
857 PHP_METHOD(HttpClient, clearHistory)
858 {
859 if (SUCCESS == zend_parse_parameters_none()) {
860 zend_update_property_null(php_http_client_class_entry, getThis(), ZEND_STRL("history") TSRMLS_CC);
861 }
862 RETVAL_ZVAL(getThis(), 1, 0);
863 }
864
865 PHP_METHOD(HttpClient, getResponseMessageClass)
866 {
867 if (SUCCESS == zend_parse_parameters_none()) {
868 RETURN_PROP(php_http_client_class_entry, "responseMessageClass");
869 }
870 RETURN_FALSE;
871 }
872
873 PHP_METHOD(HttpClient, setResponseMessageClass)
874 {
875 char *cn;
876 int cl;
877
878 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &cn, &cl)) {
879 zend_update_property_stringl(php_http_client_class_entry, getThis(), ZEND_STRL("responseMessageClass"), cn, cl TSRMLS_CC);
880 }
881 RETVAL_ZVAL(getThis(), 1, 0);
882 }
883
884 PHP_METHOD(HttpClient, setRequest)
885 {
886 zval *zreq = NULL;
887
888 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zreq, php_http_client_request_get_class_entry())) {
889 zend_update_property(php_http_client_class_entry, getThis(), ZEND_STRL("request"), zreq TSRMLS_CC);
890 }
891 RETURN_ZVAL(getThis(), 1, 0);
892 }
893
894 PHP_METHOD(HttpClient, getRequest)
895 {
896 if (SUCCESS == zend_parse_parameters_none()) {
897 RETURN_PROP(php_http_client_class_entry, "request");
898 }
899 }
900
901 PHP_METHOD(HttpClient, request)
902 {
903 char *meth_str, *url_str;
904 int meth_len, url_len;
905 zval *zheader, *zbody, *zoptions;
906
907 with_error_handling(EH_THROW, php_http_exception_get_class_entry()) {
908 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a!z!a!/", &meth_str, &meth_len, &url_str, &url_len, &zheader, &zbody, &zoptions)) {
909 php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
910 php_http_message_object_t *msg_obj;
911 zend_object_value ov;
912 zval *req, *res = NULL;
913
914 php_http_new(&ov, php_http_client_request_get_class_entry(), (php_http_new_t) php_http_message_object_new_ex, NULL, NULL, (void *) &msg_obj TSRMLS_CC);
915 MAKE_STD_ZVAL(req);
916 ZVAL_OBJVAL(req, ov, 0);
917
918 msg_obj->message = php_http_message_init(NULL, PHP_HTTP_REQUEST TSRMLS_CC);
919 PHP_HTTP_INFO(msg_obj->message).request.url = estrndup(url_str, url_len);
920 PHP_HTTP_INFO(msg_obj->message).request.method = estrndup(meth_str, meth_len);
921
922 if (zheader) {
923 array_copy(Z_ARRVAL_P(zheader), &msg_obj->message->hdrs);
924 }
925
926 if (zbody) {
927 php_http_message_object_set_body(msg_obj, zbody TSRMLS_CC);
928 }
929
930 if (zoptions) {
931 php_http_client_options_set(req, zoptions TSRMLS_CC);
932 }
933
934 zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "send", &res, req);
935 RETVAL_ZVAL(res, 0, 1);
936 zval_ptr_dtor(&req);
937 }
938 } end_error_handling();
939 }
940
941 PHP_METHOD(HttpClient, send)
942 {
943 zval *zreq = NULL;
944
945 RETVAL_FALSE;
946
947 with_error_handling(EH_THROW, php_http_exception_get_class_entry()) {
948 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O!", &zreq, php_http_client_request_get_class_entry())) {
949 if (SUCCESS == php_http_client_object_handle_request(getThis(), &zreq TSRMLS_CC)) {
950 php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
951 php_http_message_object_t *req = zend_object_store_get_object(zreq TSRMLS_CC);
952
953 php_http_client_exec(obj->client, req->message);
954
955 if (SUCCESS == php_http_client_object_handle_response(getThis() TSRMLS_CC)) {
956 RETVAL_PROP(php_http_client_class_entry, "responseMessage");
957 } else {
958 php_http_error(HE_WARNING, PHP_HTTP_E_CLIENT, "Failed to handle response");
959 }
960 } else {
961 php_http_error(HE_WARNING, PHP_HTTP_E_CLIENT, "Failed to handle request");
962 }
963 }
964 } end_error_handling();
965 }
966
967 PHP_MINIT_FUNCTION(http_client)
968 {
969 PHP_HTTP_REGISTER_CLASS(http\\Client, AbstractClient, http_client, php_http_object_get_class_entry(), ZEND_ACC_ABSTRACT);
970 php_http_client_class_entry->create_object = php_http_client_object_new;
971 memcpy(&php_http_client_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
972 php_http_client_object_handlers.clone_obj = php_http_client_object_clone;
973
974 zend_class_implements(php_http_client_class_entry TSRMLS_CC, 2, spl_ce_SplSubject, php_http_client_interface_get_class_entry());
975
976 zend_declare_property_string(php_http_client_class_entry, ZEND_STRL("responseMessageClass"), "", ZEND_ACC_PRIVATE TSRMLS_CC);
977 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("observers"), ZEND_ACC_PRIVATE TSRMLS_CC);
978 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("transferInfo"), ZEND_ACC_PRIVATE TSRMLS_CC);
979 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("responseMessage"), ZEND_ACC_PRIVATE TSRMLS_CC);
980 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("requestMessage"), ZEND_ACC_PRIVATE TSRMLS_CC);
981 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("history"), ZEND_ACC_PRIVATE TSRMLS_CC);
982
983 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("options"), ZEND_ACC_PROTECTED TSRMLS_CC);
984 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("request"), ZEND_ACC_PROTECTED TSRMLS_CC);
985
986 zend_declare_property_bool(php_http_client_class_entry, ZEND_STRL("recordHistory"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
987
988 return SUCCESS;
989 }
990
991 /*
992 * Local variables:
993 * tab-width: 4
994 * c-basic-offset: 4
995 * End:
996 * vim600: noet sw=4 ts=4 fdm=marker
997 * vim<600: noet sw=4 ts=4
998 */
999