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