merge raphf branch
[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_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_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, NULL 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, NULL 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_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_resource_factory_addref(from->rf);
96 to->rf = from->rf;
97 } else if (to->ops->rsrc){
98 to->rf = php_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, NULL 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, NULL 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 MAKE_STD_ZVAL(callback->func.user);
399 array_init(callback->func.user);
400 Z_ADDREF_P(zclient);
401 add_next_index_zval(callback->func.user, zclient);
402 add_next_index_stringl(callback->func.user, ZEND_STRL("notify"), 1);
403
404 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_PROGRESS_CALLBACK, callback);
405 }
406 progress->state.info = "start";
407 php_http_client_progress_notify(progress TSRMLS_CC);
408 progress->state.started = 1;
409 }
410
411 return SUCCESS;
412 }
413
414
415 STATUS php_http_client_object_handle_response(zval *zclient TSRMLS_DC)
416 {
417 php_http_client_object_t *obj = zend_object_store_get_object(zclient TSRMLS_CC);
418 php_http_client_progress_t *progress;
419 php_http_message_t *msg;
420 zval *info;
421
422 /* always fetch info */
423 MAKE_STD_ZVAL(info);
424 array_init(info);
425 php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_TRANSFER_INFO, Z_ARRVAL_P(info));
426 zend_update_property(php_http_client_class_entry, zclient, ZEND_STRL("transferInfo"), info TSRMLS_CC);
427 zval_ptr_dtor(&info);
428
429 if ((msg = obj->client->response.message)) {
430 if (i_zend_is_true(zend_read_property(php_http_client_class_entry, zclient, ZEND_STRL("recordHistory"), 0 TSRMLS_CC))) {
431 zval *new_hist, *old_hist = zend_read_property(php_http_client_class_entry, zclient, ZEND_STRL("history"), 0 TSRMLS_CC);
432 php_http_message_t *zipped = php_http_message_zip(obj->client->response.message, obj->client->request.message);
433 zend_object_value ov = php_http_client_object_message(zclient, zipped TSRMLS_CC);
434
435 MAKE_STD_ZVAL(new_hist);
436 ZVAL_OBJVAL(new_hist, ov, 0);
437
438 if (Z_TYPE_P(old_hist) == IS_OBJECT) {
439 php_http_message_object_prepend(new_hist, old_hist, 1 TSRMLS_CC);
440 }
441
442 zend_update_property(php_http_client_class_entry, zclient, ZEND_STRL("history"), new_hist TSRMLS_CC);
443 zval_ptr_dtor(&new_hist);
444 }
445
446 /* update response info */
447 if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, msg)) {
448 zval *message;
449
450 MAKE_STD_ZVAL(message);
451 ZVAL_OBJVAL(message, php_http_client_object_message(zclient, msg TSRMLS_CC), 0);
452 zend_update_property(php_http_client_class_entry, zclient, ZEND_STRL("responseMessage"), message TSRMLS_CC);
453 zval_ptr_dtor(&message);
454
455 obj->client->response.message = php_http_message_init(NULL, 0, NULL TSRMLS_CC);
456 } else {
457 zend_update_property_null(php_http_client_class_entry, zclient, ZEND_STRL("responseMessage") TSRMLS_CC);
458 }
459 } else {
460 zend_update_property_null(php_http_client_class_entry, zclient, ZEND_STRL("responseMessage") TSRMLS_CC);
461 }
462
463 if ((msg = obj->client->request.message)) {
464 if (PHP_HTTP_MESSAGE_TYPE(REQUEST, msg)) {
465 zval *message;
466
467 /* update the actual request message */
468 MAKE_STD_ZVAL(message);
469 ZVAL_OBJVAL(message, php_http_message_object_new_ex(php_http_message_get_class_entry(), msg, NULL TSRMLS_CC), 0);
470 zend_update_property(php_http_client_class_entry, zclient, ZEND_STRL("requestMessage"), message TSRMLS_CC);
471 zval_ptr_dtor(&message);
472 obj->client->request.message = php_http_message_init(NULL, 0, NULL TSRMLS_CC);
473 }
474 }
475
476 if (SUCCESS == php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, &progress)) {
477 progress->state.info = "finished";
478 progress->state.finished = 1;
479 php_http_client_progress_notify(progress TSRMLS_CC);
480 }
481 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_PROGRESS_CALLBACK, NULL);
482
483 return SUCCESS;
484 }
485
486 void php_http_client_options_set(zval *this_ptr, zval *opts TSRMLS_DC)
487 {
488 php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
489 HashPosition pos;
490 zval *new_opts;
491 zend_class_entry *this_ce = Z_OBJCE_P(getThis());
492 zend_bool is_client = instanceof_function(this_ce, php_http_client_class_entry TSRMLS_CC);
493
494 MAKE_STD_ZVAL(new_opts);
495 array_init(new_opts);
496
497 if (!opts || !zend_hash_num_elements(Z_ARRVAL_P(opts))) {
498 zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC);
499 zval_ptr_dtor(&new_opts);
500 } else {
501 zval *old_opts, *add_opts, **opt;
502
503 MAKE_STD_ZVAL(add_opts);
504 array_init(add_opts);
505 /* some options need extra attention -- thus cannot use array_merge() directly */
506 FOREACH_KEYVAL(pos, opts, key, opt) {
507 if (key.type == HASH_KEY_IS_STRING) {
508 #define KEYMATCH(k, s) ((sizeof(s)==k.len) && !strcasecmp(k.str, s))
509 if (Z_TYPE_PP(opt) == IS_ARRAY && (KEYMATCH(key, "ssl") || KEYMATCH(key, "cookies"))) {
510 php_http_client_options_set_subr(getThis(), key.str, key.len, *opt, 0 TSRMLS_CC);
511 } else if (is_client && (KEYMATCH(key, "recordHistory") || KEYMATCH(key, "responseMessageClass"))) {
512 zend_update_property(this_ce, getThis(), key.str, key.len-1, *opt TSRMLS_CC);
513 } else if (Z_TYPE_PP(opt) == IS_NULL) {
514 old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
515 if (Z_TYPE_P(old_opts) == IS_ARRAY) {
516 zend_symtable_del(Z_ARRVAL_P(old_opts), key.str, key.len);
517 }
518 } else {
519 Z_ADDREF_P(*opt);
520 add_assoc_zval_ex(add_opts, key.str, key.len, *opt);
521 }
522 }
523 }
524
525 old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
526 if (Z_TYPE_P(old_opts) == IS_ARRAY) {
527 array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL_P(new_opts));
528 }
529 array_join(Z_ARRVAL_P(add_opts), Z_ARRVAL_P(new_opts), 0, 0);
530 zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC);
531 zval_ptr_dtor(&new_opts);
532 zval_ptr_dtor(&add_opts);
533 }
534 }
535
536 void php_http_client_options_set_subr(zval *this_ptr, char *key, size_t len, zval *opts, int overwrite TSRMLS_DC)
537 {
538 if (overwrite || (opts && zend_hash_num_elements(Z_ARRVAL_P(opts)))) {
539 zend_class_entry *this_ce = Z_OBJCE_P(getThis());
540 zval *old_opts, *new_opts, **entry = NULL;
541
542 MAKE_STD_ZVAL(new_opts);
543 array_init(new_opts);
544 old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
545 if (Z_TYPE_P(old_opts) == IS_ARRAY) {
546 array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL_P(new_opts));
547 }
548
549 if (overwrite) {
550 if (opts && zend_hash_num_elements(Z_ARRVAL_P(opts))) {
551 Z_ADDREF_P(opts);
552 zend_symtable_update(Z_ARRVAL_P(new_opts), key, len, (void *) &opts, sizeof(zval *), NULL);
553 } else {
554 zend_symtable_del(Z_ARRVAL_P(new_opts), key, len);
555 }
556 } else if (opts && zend_hash_num_elements(Z_ARRVAL_P(opts))) {
557 if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(new_opts), key, len, (void *) &entry)) {
558 array_join(Z_ARRVAL_P(opts), Z_ARRVAL_PP(entry), 0, 0);
559 } else {
560 Z_ADDREF_P(opts);
561 zend_symtable_update(Z_ARRVAL_P(new_opts), key, len, (void *) &opts, sizeof(zval *), NULL);
562 }
563 }
564
565 zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC);
566 zval_ptr_dtor(&new_opts);
567 }
568 }
569
570 void php_http_client_options_get_subr(zval *this_ptr, char *key, size_t len, zval *return_value TSRMLS_DC)
571 {
572 zend_class_entry *this_ce = Z_OBJCE_P(getThis());
573 zval **options, *opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
574
575 if ((Z_TYPE_P(opts) == IS_ARRAY) && (SUCCESS == zend_symtable_find(Z_ARRVAL_P(opts), key, len, (void *) &options))) {
576 RETVAL_ZVAL(*options, 1, 0);
577 }
578 }
579
580 PHP_METHOD(HttpClient, __construct)
581 {
582 with_error_handling(EH_THROW, php_http_exception_get_class_entry()) {
583 zval *os, *opts = NULL;
584
585 MAKE_STD_ZVAL(os);
586 object_init_ex(os, spl_ce_SplObjectStorage);
587 zend_update_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), os TSRMLS_CC);
588 zval_ptr_dtor(&os);
589
590 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts)) {
591 php_http_client_options_set(getThis(), opts TSRMLS_CC);
592 }
593
594 } end_error_handling();
595 }
596
597 PHP_METHOD(HttpClient, getObservers)
598 {
599 with_error_handling(EH_THROW, php_http_exception_get_class_entry()) {
600 if (SUCCESS == zend_parse_parameters_none()) {
601 RETVAL_PROP(php_http_client_class_entry, "observers");
602 }
603 } end_error_handling();
604 }
605
606 static int notify(zend_object_iterator *iter, void *puser TSRMLS_DC)
607 {
608 zval **observer = NULL;
609
610 iter->funcs->get_current_data(iter, &observer TSRMLS_CC);
611 if (observer) {
612 zval *retval;
613
614 zend_call_method_with_1_params(observer, NULL, NULL, "update", &retval, puser);
615 zval_ptr_dtor(&retval);
616 return SUCCESS;
617 }
618 return FAILURE;
619 }
620
621 PHP_METHOD(HttpClient, notify)
622 {
623 if (SUCCESS == zend_parse_parameters_none()) {
624 zval *observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
625
626 if (Z_TYPE_P(observers) == IS_OBJECT) {
627 Z_ADDREF_P(getThis());
628 spl_iterator_apply(observers, notify, getThis() TSRMLS_CC);
629 zval_ptr_dtor(&getThis());
630 }
631 }
632
633 RETVAL_ZVAL(getThis(), 1, 0);
634 }
635
636 PHP_METHOD(HttpClient, attach)
637 {
638 zval *observer;
639
640 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &observer, spl_ce_SplObserver)) {
641 zval *retval, *observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
642 zend_call_method_with_1_params(&observers, NULL, NULL, "attach", &retval, observer);
643 zval_ptr_dtor(&retval);
644 }
645
646 RETVAL_ZVAL(getThis(), 1, 0);
647 }
648
649 PHP_METHOD(HttpClient, detach)
650 {
651 zval *observer;
652
653 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &observer, spl_ce_SplObserver)) {
654 zval *retval, *observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
655 zend_call_method_with_1_params(&observers, NULL, NULL, "detach", &retval, observer);
656 zval_ptr_dtor(&retval);
657 }
658
659 RETVAL_ZVAL(getThis(), 1, 0);
660 }
661
662 PHP_METHOD(HttpClient, getProgress)
663 {
664 if (SUCCESS == zend_parse_parameters_none()) {
665 php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
666 php_http_client_progress_t *progress = NULL;
667
668 if (SUCCESS == php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, &progress)) {
669 object_init(return_value);
670 add_property_bool(return_value, "started", progress->state.started);
671 add_property_bool(return_value, "finished", progress->state.finished);
672 add_property_string(return_value, "info", STR_PTR(progress->state.info), 1);
673 add_property_double(return_value, "dltotal", progress->state.dl.total);
674 add_property_double(return_value, "dlnow", progress->state.dl.now);
675 add_property_double(return_value, "ultotal", progress->state.ul.total);
676 add_property_double(return_value, "ulnow", progress->state.ul.now);
677 }
678 }
679 }
680
681 PHP_METHOD(HttpClient, getTransferInfo)
682 {
683 char *info_name = NULL;
684 int info_len = 0;
685
686 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &info_name, &info_len)) {
687 zval **infop, *temp = NULL, *info = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("transferInfo"), 0 TSRMLS_CC);
688
689 /* request completed? */
690 if (Z_TYPE_P(info) != IS_ARRAY) {
691 php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
692
693 MAKE_STD_ZVAL(temp);
694 array_init(temp);
695 php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_TRANSFER_INFO, Z_ARRVAL_P(temp));
696 info = temp;
697 }
698
699 if (info_len && info_name) {
700 if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(info), php_http_pretty_key(info_name, info_len, 0, 0), info_len + 1, (void *) &infop)) {
701 RETVAL_ZVAL(*infop, 1, 0);
702 } else {
703 php_http_error(HE_NOTICE, PHP_HTTP_E_INVALID_PARAM, "Could not find transfer info named %s", info_name);
704 RETVAL_FALSE;
705 }
706 } else {
707 RETVAL_ZVAL(info, 1, 0);
708 }
709
710 if (temp) {
711 zval_ptr_dtor(&temp);
712 }
713 return;
714 }
715 RETURN_FALSE;
716 }
717
718 PHP_METHOD(HttpClient, setOptions)
719 {
720 zval *opts = NULL;
721
722 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts)) {
723 php_http_client_options_set(getThis(), opts TSRMLS_CC);
724
725 RETVAL_ZVAL(getThis(), 1, 0);
726 }
727 }
728
729 PHP_METHOD(HttpClient, getOptions)
730 {
731 if (SUCCESS == zend_parse_parameters_none()) {
732 RETURN_PROP(php_http_client_class_entry, "options");
733 }
734 RETURN_FALSE;
735 }
736
737 PHP_METHOD(HttpClient, setSslOptions)
738 {
739 zval *opts = NULL;
740
741 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts)) {
742 php_http_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 1 TSRMLS_CC);
743
744 RETVAL_ZVAL(getThis(), 1, 0);
745 }
746 }
747
748 PHP_METHOD(HttpClient, addSslOptions)
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("ssl"), opts, 0 TSRMLS_CC);
754
755 RETVAL_ZVAL(getThis(), 1, 0);
756 }
757 }
758
759 PHP_METHOD(HttpClient, getSslOptions)
760 {
761 if (SUCCESS == zend_parse_parameters_none()) {
762 php_http_client_options_get_subr(getThis(), ZEND_STRS("ssl"), return_value TSRMLS_CC);
763 }
764 }
765
766 PHP_METHOD(HttpClient, setCookies)
767 {
768 zval *opts = NULL;
769
770 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts)) {
771 php_http_client_options_set_subr(getThis(), ZEND_STRS("cookies"), opts, 1 TSRMLS_CC);
772
773 RETVAL_ZVAL(getThis(), 1, 0);
774 }
775 }
776
777 PHP_METHOD(HttpClient, addCookies)
778 {
779 zval *opts = NULL;
780
781 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts)) {
782 php_http_client_options_set_subr(getThis(), ZEND_STRS("cookies"), opts, 0 TSRMLS_CC);
783
784 RETVAL_ZVAL(getThis(), 1, 0);
785 }
786 }
787
788 PHP_METHOD(HttpClient, getCookies)
789 {
790 if (SUCCESS == zend_parse_parameters_none()) {
791 php_http_client_options_get_subr(getThis(), ZEND_STRS("cookies"), return_value TSRMLS_CC);
792 }
793 }
794
795 PHP_METHOD(HttpClient, enableCookies)
796 {
797 if (SUCCESS == zend_parse_parameters_none()){
798 php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
799
800 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_COOKIES_ENABLE, NULL);
801 }
802 RETVAL_ZVAL(getThis(), 1, 0);
803 }
804
805 PHP_METHOD(HttpClient, resetCookies)
806 {
807 zend_bool session_only = 0;
808
809 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &session_only)) {
810 php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
811 if (session_only) {
812 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_COOKIES_RESET_SESSION, NULL);
813 } else {
814 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_COOKIES_RESET, NULL);
815 }
816 }
817 RETVAL_ZVAL(getThis(), 1, 0);
818 }
819
820 PHP_METHOD(HttpClient, flushCookies)
821 {
822 if (SUCCESS == zend_parse_parameters_none()) {
823 php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
824
825 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_COOKIES_FLUSH, NULL);
826 }
827 RETVAL_ZVAL(getThis(), 1, 0);
828 }
829
830 PHP_METHOD(HttpClient, getResponseMessage)
831 {
832 with_error_handling(EH_THROW, php_http_exception_get_class_entry()) {
833 if (SUCCESS == zend_parse_parameters_none()) {
834 zval *message = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("responseMessage"), 0 TSRMLS_CC);
835
836 if (Z_TYPE_P(message) == IS_OBJECT) {
837 RETVAL_OBJECT(message, 1);
838 } else {
839 php_http_error(HE_WARNING, PHP_HTTP_E_RUNTIME, "HttpClient does not contain a response message");
840 }
841 }
842 } end_error_handling();
843 }
844
845 PHP_METHOD(HttpClient, getRequestMessage)
846 {
847 with_error_handling(EH_THROW, php_http_exception_get_class_entry()) {
848 if (SUCCESS == zend_parse_parameters_none()) {
849 zval *message = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("requestMessage"), 0 TSRMLS_CC);
850
851 if (Z_TYPE_P(message) == IS_OBJECT) {
852 RETVAL_OBJECT(message, 1);
853 } else {
854 php_http_error(HE_WARNING, PHP_HTTP_E_RUNTIME, "HttpClient does not contain a request message");
855 }
856 }
857 } end_error_handling();
858 }
859
860 PHP_METHOD(HttpClient, getHistory)
861 {
862 with_error_handling(EH_THROW, php_http_exception_get_class_entry()) {
863 if (SUCCESS == zend_parse_parameters_none()) {
864 zval *hist = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("history"), 0 TSRMLS_CC);
865
866 if (Z_TYPE_P(hist) == IS_OBJECT) {
867 RETVAL_OBJECT(hist, 1);
868 } else {
869 php_http_error(HE_WARNING, PHP_HTTP_E_RUNTIME, "The history is empty");
870 }
871 }
872 } end_error_handling();
873 }
874
875 PHP_METHOD(HttpClient, clearHistory)
876 {
877 if (SUCCESS == zend_parse_parameters_none()) {
878 zend_update_property_null(php_http_client_class_entry, getThis(), ZEND_STRL("history") TSRMLS_CC);
879 }
880 RETVAL_ZVAL(getThis(), 1, 0);
881 }
882
883 PHP_METHOD(HttpClient, getResponseMessageClass)
884 {
885 if (SUCCESS == zend_parse_parameters_none()) {
886 RETURN_PROP(php_http_client_class_entry, "responseMessageClass");
887 }
888 RETURN_FALSE;
889 }
890
891 PHP_METHOD(HttpClient, setResponseMessageClass)
892 {
893 char *cn;
894 int cl;
895
896 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &cn, &cl)) {
897 zend_update_property_stringl(php_http_client_class_entry, getThis(), ZEND_STRL("responseMessageClass"), cn, cl TSRMLS_CC);
898 }
899 RETVAL_ZVAL(getThis(), 1, 0);
900 }
901
902 PHP_METHOD(HttpClient, setRequest)
903 {
904 zval *zreq = NULL;
905
906 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zreq, php_http_client_request_get_class_entry())) {
907 zend_update_property(php_http_client_class_entry, getThis(), ZEND_STRL("request"), zreq TSRMLS_CC);
908 }
909 RETURN_ZVAL(getThis(), 1, 0);
910 }
911
912 PHP_METHOD(HttpClient, getRequest)
913 {
914 if (SUCCESS == zend_parse_parameters_none()) {
915 RETURN_PROP(php_http_client_class_entry, "request");
916 }
917 }
918
919 PHP_METHOD(HttpClient, request)
920 {
921 char *meth_str, *url_str;
922 int meth_len, url_len;
923 zval *zheader, *zbody, *zoptions;
924
925 with_error_handling(EH_THROW, php_http_exception_get_class_entry()) {
926 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)) {
927 php_http_message_object_t *msg_obj;
928 zend_object_value ov;
929 zval *req, *res = NULL;
930
931 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);
932 MAKE_STD_ZVAL(req);
933 ZVAL_OBJVAL(req, ov, 0);
934
935 msg_obj->message = php_http_message_init(NULL, PHP_HTTP_REQUEST, NULL TSRMLS_CC);
936 PHP_HTTP_INFO(msg_obj->message).request.url = estrndup(url_str, url_len);
937 PHP_HTTP_INFO(msg_obj->message).request.method = estrndup(meth_str, meth_len);
938
939 if (zheader) {
940 array_copy(Z_ARRVAL_P(zheader), &msg_obj->message->hdrs);
941 }
942
943 if (zbody) {
944 php_http_message_object_set_body(msg_obj, zbody TSRMLS_CC);
945 }
946
947 if (zoptions) {
948 php_http_client_options_set(req, zoptions TSRMLS_CC);
949 }
950
951 zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "send", &res, req);
952 RETVAL_ZVAL(res, 0, 1);
953 zval_ptr_dtor(&req);
954 }
955 } end_error_handling();
956 }
957
958 PHP_METHOD(HttpClient, send)
959 {
960 zval *zreq = NULL;
961
962 RETVAL_FALSE;
963
964 with_error_handling(EH_THROW, php_http_exception_get_class_entry()) {
965 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|O!", &zreq, php_http_client_request_get_class_entry())) {
966 if (SUCCESS == php_http_client_object_handle_request(getThis(), &zreq TSRMLS_CC)) {
967 php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
968 php_http_message_object_t *req = zend_object_store_get_object(zreq TSRMLS_CC);
969
970 php_http_client_exec(obj->client, req->message);
971
972 if (SUCCESS == php_http_client_object_handle_response(getThis() TSRMLS_CC)) {
973 RETVAL_PROP(php_http_client_class_entry, "responseMessage");
974 } else {
975 php_http_error(HE_WARNING, PHP_HTTP_E_CLIENT, "Failed to handle response");
976 }
977 } else {
978 php_http_error(HE_WARNING, PHP_HTTP_E_CLIENT, "Failed to handle request");
979 }
980 }
981 } end_error_handling();
982 }
983
984 PHP_MINIT_FUNCTION(http_client)
985 {
986 PHP_HTTP_REGISTER_CLASS(http\\Client, AbstractClient, http_client, php_http_object_get_class_entry(), ZEND_ACC_EXPLICIT_ABSTRACT_CLASS);
987 php_http_client_class_entry->create_object = php_http_client_object_new;
988 memcpy(&php_http_client_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
989 php_http_client_object_handlers.clone_obj = php_http_client_object_clone;
990
991 zend_class_implements(php_http_client_class_entry TSRMLS_CC, 2, spl_ce_SplSubject, php_http_client_interface_get_class_entry());
992
993 zend_declare_property_string(php_http_client_class_entry, ZEND_STRL("responseMessageClass"), "", ZEND_ACC_PRIVATE TSRMLS_CC);
994 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("observers"), ZEND_ACC_PRIVATE TSRMLS_CC);
995 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("transferInfo"), ZEND_ACC_PRIVATE TSRMLS_CC);
996 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("responseMessage"), ZEND_ACC_PRIVATE TSRMLS_CC);
997 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("requestMessage"), ZEND_ACC_PRIVATE TSRMLS_CC);
998 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("history"), ZEND_ACC_PRIVATE TSRMLS_CC);
999
1000 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("options"), ZEND_ACC_PROTECTED TSRMLS_CC);
1001 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("request"), ZEND_ACC_PROTECTED TSRMLS_CC);
1002
1003 zend_declare_property_bool(php_http_client_class_entry, ZEND_STRL("recordHistory"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
1004
1005 return SUCCESS;
1006 }
1007
1008 /*
1009 * Local variables:
1010 * tab-width: 4
1011 * c-basic-offset: 4
1012 * End:
1013 * vim600: noet sw=4 ts=4 fdm=marker
1014 * vim<600: noet sw=4 ts=4
1015 */
1016