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