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