don't crash if user extends abstract classes
[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_class_entry, msg, NULL TSRMLS_CC))) {
322 return ov;
323 } else {
324 return php_http_message_object_new_ex(php_http_client_response_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_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 request 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
358 /* set progress callback */
359 if (SUCCESS == php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, &progress)) {
360 if (!progress->callback) {
361 php_http_client_progress_callback_t *callback = emalloc(sizeof(*callback));
362
363 callback->type = PHP_HTTP_CLIENT_PROGRESS_CALLBACK_USER;
364 callback->pass_state = 0;
365 MAKE_STD_ZVAL(callback->func.user);
366 array_init(callback->func.user);
367 Z_ADDREF_P(zclient);
368 add_next_index_zval(callback->func.user, zclient);
369 add_next_index_stringl(callback->func.user, ZEND_STRL("notify"), 1);
370
371 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_PROGRESS_CALLBACK, callback);
372 }
373 progress->state.info = "start";
374 php_http_client_progress_notify(progress TSRMLS_CC);
375 progress->state.started = 1;
376 }
377
378 return SUCCESS;
379 }
380
381
382 STATUS php_http_client_object_handle_response(zval *zclient TSRMLS_DC)
383 {
384 php_http_client_object_t *obj = zend_object_store_get_object(zclient TSRMLS_CC);
385 php_http_client_progress_t *progress;
386 php_http_message_t *msg;
387 zval *info;
388
389 /* always fetch info */
390 MAKE_STD_ZVAL(info);
391 array_init(info);
392 php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_TRANSFER_INFO, Z_ARRVAL_P(info));
393 zend_update_property(php_http_client_class_entry, zclient, ZEND_STRL("transferInfo"), info TSRMLS_CC);
394 zval_ptr_dtor(&info);
395
396 if ((msg = obj->client->message)) {
397 /* update history */
398 if (i_zend_is_true(zend_read_property(php_http_client_class_entry, zclient, ZEND_STRL("recordHistory"), 0 TSRMLS_CC))) {
399 zval *new_hist, *old_hist = zend_read_property(php_http_client_class_entry, zclient, ZEND_STRL("history"), 0 TSRMLS_CC);
400 zend_object_value ov = php_http_client_object_message(zclient, php_http_message_copy(msg, NULL) TSRMLS_CC);
401
402 MAKE_STD_ZVAL(new_hist);
403 ZVAL_OBJVAL(new_hist, ov, 0);
404
405 if (Z_TYPE_P(old_hist) == IS_OBJECT) {
406 php_http_message_object_prepend(new_hist, old_hist, 1 TSRMLS_CC);
407 }
408
409 zend_update_property(php_http_client_class_entry, zclient, ZEND_STRL("history"), new_hist TSRMLS_CC);
410 zval_ptr_dtor(&new_hist);
411 }
412
413 /* update response info */
414 if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, msg)) {
415 zval *message;
416
417 MAKE_STD_ZVAL(message);
418 ZVAL_OBJVAL(message, php_http_client_object_message(zclient, msg TSRMLS_CC), 0);
419 zend_update_property(php_http_client_class_entry, zclient, ZEND_STRL("responseMessage"), message TSRMLS_CC);
420 zval_ptr_dtor(&message);
421
422 obj->client->message = php_http_message_init(NULL, 0 TSRMLS_CC);
423 msg = msg->parent;
424 } else {
425 zend_update_property_null(php_http_client_class_entry, zclient, ZEND_STRL("responseMessage") TSRMLS_CC);
426 }
427 } else {
428 zend_update_property_null(php_http_client_class_entry, zclient, ZEND_STRL("responseMessage") TSRMLS_CC);
429 }
430
431 /* there might be a 100-Continue response in between */
432 while (msg && !PHP_HTTP_MESSAGE_TYPE(REQUEST, msg)) {
433 msg = msg->parent;
434 }
435
436 if (PHP_HTTP_MESSAGE_TYPE(REQUEST, msg)) {
437 zval *message;
438
439 /* update the actual request message */
440 MAKE_STD_ZVAL(message);
441 ZVAL_OBJVAL(message, php_http_message_object_new_ex(php_http_message_class_entry, php_http_message_copy_ex(msg, NULL, 0), NULL TSRMLS_CC), 0);
442 zend_update_property(php_http_client_class_entry, zclient, ZEND_STRL("requestMessage"), message TSRMLS_CC);
443 zval_ptr_dtor(&message);
444 }
445
446 if (SUCCESS == php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, &progress)) {
447 progress->state.info = "finished";
448 progress->state.finished = 1;
449 php_http_client_progress_notify(progress TSRMLS_CC);
450 }
451 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_PROGRESS_CALLBACK, NULL);
452
453 return SUCCESS;
454 }
455
456 static int apply_pretty_key(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
457 {
458 zval **zpp = pDest, *arr = va_arg(args, zval *);
459
460 if (hash_key->arKey && hash_key->nKeyLength > 1) {
461 char *tmp = php_http_pretty_key(estrndup(hash_key->arKey, hash_key->nKeyLength - 1), hash_key->nKeyLength - 1, 1, 0);
462
463 Z_ADDREF_PP(zpp);
464 add_assoc_zval_ex(arr, tmp, hash_key->nKeyLength, *zpp);
465 efree(tmp);
466 }
467 return ZEND_HASH_APPLY_KEEP;
468 }
469
470 static void php_http_client_object_set_options(INTERNAL_FUNCTION_PARAMETERS)
471 {
472 php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
473 HashPosition pos;
474 zval *opts = NULL, *old_opts, *new_opts, *add_opts, **opt;
475
476 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts)) {
477 RETURN_FALSE;
478 }
479
480 MAKE_STD_ZVAL(new_opts);
481 array_init(new_opts);
482
483 if (!opts || !zend_hash_num_elements(Z_ARRVAL_P(opts))) {
484 zend_update_property(php_http_client_class_entry, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC);
485 zval_ptr_dtor(&new_opts);
486 } else {
487 MAKE_STD_ZVAL(add_opts);
488 array_init(add_opts);
489 /* some options need extra attention -- thus cannot use array_merge() directly */
490 FOREACH_KEYVAL(pos, opts, key, opt) {
491 if (key.type == HASH_KEY_IS_STRING) {
492 #define KEYMATCH(k, s) ((sizeof(s)==k.len) && !strcasecmp(k.str, s))
493 if (KEYMATCH(key, "ssl")) {
494 zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "addssloptions", NULL, *opt);
495 } else if (KEYMATCH(key, "cookies")) {
496 zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "addcookies", NULL, *opt);
497 } else if (KEYMATCH(key, "recordHistory")) {
498 zend_update_property(php_http_client_class_entry, getThis(), ZEND_STRL("recordHistory"), *opt TSRMLS_CC);
499 } else if (KEYMATCH(key, "responseMessageClass")) {
500 zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "setmessageclass", NULL, *opt);
501 } else if (Z_TYPE_PP(opt) == IS_NULL) {
502 old_opts = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
503 if (Z_TYPE_P(old_opts) == IS_ARRAY) {
504 zend_symtable_del(Z_ARRVAL_P(old_opts), key.str, key.len);
505 }
506 } else {
507 Z_ADDREF_P(*opt);
508 add_assoc_zval_ex(add_opts, key.str, key.len, *opt);
509 }
510 }
511 }
512
513 old_opts = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
514 if (Z_TYPE_P(old_opts) == IS_ARRAY) {
515 array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL_P(new_opts));
516 }
517 array_join(Z_ARRVAL_P(add_opts), Z_ARRVAL_P(new_opts), 0, 0);
518 zend_update_property(php_http_client_class_entry, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC);
519 zval_ptr_dtor(&new_opts);
520 zval_ptr_dtor(&add_opts);
521 }
522
523 RETVAL_ZVAL(getThis(), 1, 0);
524 }
525
526 static inline void php_http_client_object_set_options_subr(INTERNAL_FUNCTION_PARAMETERS, char *key, size_t len, int overwrite, int prettify_keys)
527 {
528 zval *old_opts, *new_opts, *opts = NULL, **entry = NULL;
529
530 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a/!", &opts)) {
531 MAKE_STD_ZVAL(new_opts);
532 array_init(new_opts);
533 old_opts = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
534 if (Z_TYPE_P(old_opts) == IS_ARRAY) {
535 array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL_P(new_opts));
536 }
537
538 if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(new_opts), key, len, (void *) &entry)) {
539 if (overwrite) {
540 zend_hash_clean(Z_ARRVAL_PP(entry));
541 }
542 if (opts && zend_hash_num_elements(Z_ARRVAL_P(opts))) {
543 if (overwrite) {
544 array_copy(Z_ARRVAL_P(opts), Z_ARRVAL_PP(entry));
545 } else {
546 array_join(Z_ARRVAL_P(opts), Z_ARRVAL_PP(entry), 0, prettify_keys ? ARRAY_JOIN_PRETTIFY : 0);
547 }
548 }
549 } else if (opts) {
550 if (prettify_keys) {
551 zval *tmp;
552
553 MAKE_STD_ZVAL(tmp);
554 array_init_size(tmp, zend_hash_num_elements(Z_ARRVAL_P(opts)));
555 zend_hash_apply_with_arguments(Z_ARRVAL_P(opts) TSRMLS_CC, apply_pretty_key, 1, tmp);
556 opts = tmp;
557 } else {
558 Z_ADDREF_P(opts);
559 }
560 add_assoc_zval_ex(new_opts, key, len, opts);
561 }
562 zend_update_property(php_http_client_class_entry, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC);
563 zval_ptr_dtor(&new_opts);
564 }
565
566 RETVAL_ZVAL(getThis(), 1, 0);
567 }
568
569 static inline void php_http_client_object_get_options_subr(INTERNAL_FUNCTION_PARAMETERS, char *key, size_t len)
570 {
571 if (SUCCESS == zend_parse_parameters_none()) {
572 zval *opts, **options;
573
574 opts = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC);
575 array_init(return_value);
576
577 if ( (Z_TYPE_P(opts) == IS_ARRAY) &&
578 (SUCCESS == zend_symtable_find(Z_ARRVAL_P(opts), key, len, (void *) &options))) {
579 convert_to_array(*options);
580 array_copy(Z_ARRVAL_PP(options), Z_ARRVAL_P(return_value));
581 }
582 }
583 }
584
585 PHP_METHOD(HttpClient, __construct)
586 {
587 with_error_handling(EH_THROW, php_http_exception_class_entry) {
588 zval *os;
589
590 MAKE_STD_ZVAL(os);
591 object_init_ex(os, spl_ce_SplObjectStorage);
592 zend_update_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), os TSRMLS_CC);
593 zval_ptr_dtor(&os);
594
595 php_http_client_object_set_options(INTERNAL_FUNCTION_PARAM_PASSTHRU);
596 } end_error_handling();
597 }
598
599 PHP_METHOD(HttpClient, getObservers)
600 {
601 with_error_handling(EH_THROW, php_http_exception_class_entry) {
602 if (SUCCESS == zend_parse_parameters_none()) {
603 RETVAL_PROP(php_http_client_class_entry, "observers");
604 }
605 } end_error_handling();
606 }
607
608 static int notify(zend_object_iterator *iter, void *puser TSRMLS_DC)
609 {
610 zval **observer = NULL;
611
612 iter->funcs->get_current_data(iter, &observer TSRMLS_CC);
613 if (observer) {
614 zval *retval;
615
616 zend_call_method_with_1_params(observer, NULL, NULL, "update", &retval, puser);
617 zval_ptr_dtor(&retval);
618 return SUCCESS;
619 }
620 return FAILURE;
621 }
622
623 PHP_METHOD(HttpClient, notify)
624 {
625 if (SUCCESS == zend_parse_parameters_none()) {
626 zval *observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
627
628 if (Z_TYPE_P(observers) == IS_OBJECT) {
629 Z_ADDREF_P(getThis());
630 spl_iterator_apply(observers, notify, getThis() TSRMLS_CC);
631 zval_ptr_dtor(&getThis());
632 }
633 }
634
635 RETVAL_ZVAL(getThis(), 1, 0);
636 }
637
638 PHP_METHOD(HttpClient, attach)
639 {
640 zval *observer;
641
642 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &observer, spl_ce_SplObserver)) {
643 zval *retval, *observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
644 zend_call_method_with_1_params(&observers, NULL, NULL, "attach", &retval, observer);
645 zval_ptr_dtor(&retval);
646 }
647
648 RETVAL_ZVAL(getThis(), 1, 0);
649 }
650
651 PHP_METHOD(HttpClient, detach)
652 {
653 zval *observer;
654
655 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &observer, spl_ce_SplObserver)) {
656 zval *retval, *observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
657 zend_call_method_with_1_params(&observers, NULL, NULL, "detach", &retval, observer);
658 zval_ptr_dtor(&retval);
659 }
660
661 RETVAL_ZVAL(getThis(), 1, 0);
662 }
663
664 PHP_METHOD(HttpClient, getProgress)
665 {
666 if (SUCCESS == zend_parse_parameters_none()) {
667 php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
668 php_http_client_progress_t *progress = NULL;
669
670 if (SUCCESS == php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, &progress)) {
671 object_init(return_value);
672 add_property_bool(return_value, "started", progress->state.started);
673 add_property_bool(return_value, "finished", progress->state.finished);
674 add_property_string(return_value, "info", STR_PTR(progress->state.info), 1);
675 add_property_double(return_value, "dltotal", progress->state.dl.total);
676 add_property_double(return_value, "dlnow", progress->state.dl.now);
677 add_property_double(return_value, "ultotal", progress->state.ul.total);
678 add_property_double(return_value, "ulnow", progress->state.ul.now);
679 }
680 }
681 }
682
683 PHP_METHOD(HttpClient, getTransferInfo)
684 {
685 char *info_name = NULL;
686 int info_len = 0;
687
688 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &info_name, &info_len)) {
689 zval **infop, *temp = NULL, *info = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("transferInfo"), 0 TSRMLS_CC);
690
691 /* request completed? */
692 if (Z_TYPE_P(info) != IS_ARRAY) {
693 php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
694
695 MAKE_STD_ZVAL(temp);
696 array_init(temp);
697 php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_TRANSFER_INFO, Z_ARRVAL_P(temp));
698 info = temp;
699 }
700
701 if (info_len && info_name) {
702 if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(info), php_http_pretty_key(info_name, info_len, 0, 0), info_len + 1, (void *) &infop)) {
703 RETVAL_ZVAL(*infop, 1, 0);
704 } else {
705 php_http_error(HE_NOTICE, PHP_HTTP_E_INVALID_PARAM, "Could not find transfer info named %s", info_name);
706 RETVAL_FALSE;
707 }
708 } else {
709 RETVAL_ZVAL(info, 1, 0);
710 }
711
712 if (temp) {
713 zval_ptr_dtor(&temp);
714 }
715 return;
716 }
717 RETURN_FALSE;
718 }
719
720 PHP_METHOD(HttpClient, setOptions)
721 {
722 php_http_client_object_set_options(INTERNAL_FUNCTION_PARAM_PASSTHRU);
723 }
724
725 PHP_METHOD(HttpClient, getOptions)
726 {
727 if (SUCCESS == zend_parse_parameters_none()) {
728 RETURN_PROP(php_http_client_class_entry, "options");
729 }
730 RETURN_FALSE;
731 }
732
733 PHP_METHOD(HttpClient, setSslOptions)
734 {
735 php_http_client_object_set_options_subr(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRS("ssl"), 1, 0);
736 }
737
738 PHP_METHOD(HttpClient, addSslOptions)
739 {
740 php_http_client_object_set_options_subr(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRS("ssl"), 0, 0);
741 }
742
743 PHP_METHOD(HttpClient, getSslOptions)
744 {
745 php_http_client_object_get_options_subr(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRS("ssl"));
746 }
747
748 PHP_METHOD(HttpClient, setCookies)
749 {
750 php_http_client_object_set_options_subr(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRS("cookies"), 1, 0);
751 }
752
753 PHP_METHOD(HttpClient, addCookies)
754 {
755 php_http_client_object_set_options_subr(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRS("cookies"), 0, 0);
756 }
757
758 PHP_METHOD(HttpClient, getCookies)
759 {
760 php_http_client_object_get_options_subr(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRS("cookies"));
761 }
762
763 PHP_METHOD(HttpClient, enableCookies)
764 {
765 if (SUCCESS == zend_parse_parameters_none()){
766 php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
767
768 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_COOKIES_ENABLE, NULL);
769 }
770 RETVAL_ZVAL(getThis(), 1, 0);
771 }
772
773 PHP_METHOD(HttpClient, resetCookies)
774 {
775 zend_bool session_only = 0;
776
777 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &session_only)) {
778 php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
779 if (session_only) {
780 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_COOKIES_RESET_SESSION, NULL);
781 } else {
782 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_COOKIES_RESET, NULL);
783 }
784 }
785 RETVAL_ZVAL(getThis(), 1, 0);
786 }
787
788 PHP_METHOD(HttpClient, flushCookies)
789 {
790 if (SUCCESS == zend_parse_parameters_none()) {
791 php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
792
793 php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_COOKIES_FLUSH, NULL);
794 }
795 RETVAL_ZVAL(getThis(), 1, 0);
796 }
797
798 PHP_METHOD(HttpClient, getResponseMessage)
799 {
800 with_error_handling(EH_THROW, php_http_exception_class_entry) {
801 if (SUCCESS == zend_parse_parameters_none()) {
802 zval *message = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("responseMessage"), 0 TSRMLS_CC);
803
804 if (Z_TYPE_P(message) == IS_OBJECT) {
805 RETVAL_OBJECT(message, 1);
806 } else {
807 php_http_error(HE_WARNING, PHP_HTTP_E_RUNTIME, "HttpClient does not contain a response message");
808 }
809 }
810 } end_error_handling();
811 }
812
813 PHP_METHOD(HttpClient, getRequestMessage)
814 {
815 with_error_handling(EH_THROW, php_http_exception_class_entry) {
816 if (SUCCESS == zend_parse_parameters_none()) {
817 zval *message = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("requestMessage"), 0 TSRMLS_CC);
818
819 if (Z_TYPE_P(message) == IS_OBJECT) {
820 RETVAL_OBJECT(message, 1);
821 } else {
822 php_http_error(HE_WARNING, PHP_HTTP_E_RUNTIME, "HttpClient does not contain a request message");
823 }
824 }
825 } end_error_handling();
826 }
827
828 PHP_METHOD(HttpClient, getHistory)
829 {
830 with_error_handling(EH_THROW, php_http_exception_class_entry) {
831 if (SUCCESS == zend_parse_parameters_none()) {
832 zval *hist = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("history"), 0 TSRMLS_CC);
833
834 if (Z_TYPE_P(hist) == IS_OBJECT) {
835 RETVAL_OBJECT(hist, 1);
836 } else {
837 php_http_error(HE_WARNING, PHP_HTTP_E_RUNTIME, "The history is empty");
838 }
839 }
840 } end_error_handling();
841 }
842
843 PHP_METHOD(HttpClient, clearHistory)
844 {
845 if (SUCCESS == zend_parse_parameters_none()) {
846 zend_update_property_null(php_http_client_class_entry, getThis(), ZEND_STRL("history") TSRMLS_CC);
847 }
848 RETVAL_ZVAL(getThis(), 1, 0);
849 }
850
851 PHP_METHOD(HttpClient, getResponseMessageClass)
852 {
853 if (SUCCESS == zend_parse_parameters_none()) {
854 RETURN_PROP(php_http_client_class_entry, "responseMessageClass");
855 }
856 RETURN_FALSE;
857 }
858
859 PHP_METHOD(HttpClient, setResponseMessageClass)
860 {
861 char *cn;
862 int cl;
863
864 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &cn, &cl)) {
865 zend_update_property_stringl(php_http_client_class_entry, getThis(), ZEND_STRL("responseMessageClass"), cn, cl TSRMLS_CC);
866 }
867 RETVAL_ZVAL(getThis(), 1, 0);
868 }
869
870 PHP_METHOD(HttpClient, setRequest)
871 {
872 zval *zreq = NULL;
873
874 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zreq, php_http_client_request_class_entry)) {
875 zend_update_property(php_http_client_class_entry, getThis(), ZEND_STRL("request"), zreq TSRMLS_CC);
876 }
877 RETURN_ZVAL(getThis(), 1, 0);
878 }
879
880 PHP_METHOD(HttpClient, getRequest)
881 {
882 if (SUCCESS == zend_parse_parameters_none()) {
883 RETURN_PROP(php_http_client_class_entry, "request");
884 }
885 }
886
887 PHP_METHOD(HttpClient, send)
888 {
889 zval *zreq = NULL;
890
891 RETVAL_FALSE;
892
893 with_error_handling(EH_THROW, php_http_exception_class_entry) {
894 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O!", &zreq, php_http_client_request_class_entry)) {
895 if (SUCCESS == php_http_client_object_handle_request(getThis(), &zreq TSRMLS_CC)) {
896 php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
897 php_http_message_object_t *req = zend_object_store_get_object(zreq TSRMLS_CC);
898
899 php_http_client_exec(obj->client, req->message);
900
901 if (SUCCESS == php_http_client_object_handle_response(getThis() TSRMLS_CC)) {
902 RETVAL_PROP(php_http_client_class_entry, "responseMessage");
903 } else {
904 php_http_error(HE_WARNING, PHP_HTTP_E_CLIENT, "Failed to handle response");
905 }
906 } else {
907 php_http_error(HE_WARNING, PHP_HTTP_E_CLIENT, "Failed to handle request");
908 }
909 }
910 } end_error_handling();
911 }
912
913 PHP_MINIT_FUNCTION(http_client)
914 {
915 PHP_HTTP_REGISTER_CLASS(http\\Client, AbstractClient, http_client, php_http_object_class_entry, ZEND_ACC_ABSTRACT);
916 php_http_client_class_entry->create_object = php_http_client_object_new;
917 memcpy(&php_http_client_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
918 php_http_client_object_handlers.clone_obj = php_http_client_object_clone;
919
920 zend_class_implements(php_http_client_class_entry TSRMLS_CC, 2, spl_ce_SplSubject, php_http_client_interface_class_entry);
921
922 zend_declare_property_string(php_http_client_class_entry, ZEND_STRL("responseMessageClass"), "", ZEND_ACC_PRIVATE TSRMLS_CC);
923 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("observers"), ZEND_ACC_PRIVATE TSRMLS_CC);
924 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("options"), ZEND_ACC_PRIVATE TSRMLS_CC);
925 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("transferInfo"), ZEND_ACC_PRIVATE TSRMLS_CC);
926 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("responseMessage"), ZEND_ACC_PRIVATE TSRMLS_CC);
927 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("requestMessage"), ZEND_ACC_PRIVATE TSRMLS_CC);
928 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("history"), ZEND_ACC_PRIVATE TSRMLS_CC);
929
930 zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("request"), ZEND_ACC_PROTECTED TSRMLS_CC);
931
932 zend_declare_property_bool(php_http_client_class_entry, ZEND_STRL("recordHistory"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
933
934 return SUCCESS;
935 }
936
937 /*
938 * Local variables:
939 * tab-width: 4
940 * c-basic-offset: 4
941 * End:
942 * vim600: noet sw=4 ts=4 fdm=marker
943 * vim<600: noet sw=4 ts=4
944 */
945