- adjust ini entry names to those of the globals struct
[m6w6/ext-http] / http_message_object.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-2006, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #define HTTP_WANT_SAPI
16 #define HTTP_WANT_CURL
17 #include "php_http.h"
18
19 #ifdef ZEND_ENGINE_2
20
21 #include "zend_interfaces.h"
22 #include "ext/standard/url.h"
23
24 #include "php_http_api.h"
25 #include "php_http_send_api.h"
26 #include "php_http_url_api.h"
27 #include "php_http_message_api.h"
28 #include "php_http_message_object.h"
29 #include "php_http_exception_object.h"
30 #include "php_http_response_object.h"
31 #include "php_http_request_method_api.h"
32 #include "php_http_request_api.h"
33 #include "php_http_request_object.h"
34
35 #ifndef WONKY
36 # ifdef HAVE_SPL
37 /* SPL doesn't install its headers */
38 extern PHPAPI zend_class_entry *spl_ce_Countable;
39 # endif
40 #endif
41
42 #define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpMessage, method, 0, req_args)
43 #define HTTP_EMPTY_ARGS(method) HTTP_EMPTY_ARGS_EX(HttpMessage, method, 0)
44 #define HTTP_MESSAGE_ME(method, visibility) PHP_ME(HttpMessage, method, HTTP_ARGS(HttpMessage, method), visibility)
45
46 HTTP_BEGIN_ARGS(__construct, 0)
47 HTTP_ARG_VAL(message, 0)
48 HTTP_END_ARGS;
49
50 HTTP_BEGIN_ARGS(fromString, 1)
51 HTTP_ARG_VAL(message, 0)
52 HTTP_END_ARGS;
53
54 HTTP_EMPTY_ARGS(getBody);
55 HTTP_BEGIN_ARGS(setBody, 1)
56 HTTP_ARG_VAL(body, 0)
57 HTTP_END_ARGS;
58
59 HTTP_EMPTY_ARGS(getHeaders);
60 HTTP_BEGIN_ARGS(setHeaders, 1)
61 HTTP_ARG_VAL(headers, 0)
62 HTTP_END_ARGS;
63
64 HTTP_BEGIN_ARGS(addHeaders, 1)
65 HTTP_ARG_VAL(headers, 0)
66 HTTP_ARG_VAL(append, 0)
67 HTTP_END_ARGS;
68
69 HTTP_EMPTY_ARGS(getType);
70 HTTP_BEGIN_ARGS(setType, 1)
71 HTTP_ARG_VAL(type, 0)
72 HTTP_END_ARGS;
73
74 HTTP_EMPTY_ARGS(getResponseCode);
75 HTTP_BEGIN_ARGS(setResponseCode, 1)
76 HTTP_ARG_VAL(response_code, 0)
77 HTTP_END_ARGS;
78
79 HTTP_EMPTY_ARGS(getResponseStatus);
80 HTTP_BEGIN_ARGS(setResponseStatus, 1)
81 HTTP_ARG_VAL(response_status, 0)
82 HTTP_END_ARGS;
83
84 HTTP_EMPTY_ARGS(getRequestMethod);
85 HTTP_BEGIN_ARGS(setRequestMethod, 1)
86 HTTP_ARG_VAL(request_method, 0)
87 HTTP_END_ARGS;
88
89 HTTP_EMPTY_ARGS(getRequestUrl);
90 HTTP_BEGIN_ARGS(setRequestUrl, 1)
91 HTTP_ARG_VAL(url, 0)
92 HTTP_END_ARGS;
93
94 HTTP_EMPTY_ARGS(getHttpVersion);
95 HTTP_BEGIN_ARGS(setHttpVersion, 1)
96 HTTP_ARG_VAL(http_version, 0)
97 HTTP_END_ARGS;
98
99 HTTP_EMPTY_ARGS(getParentMessage);
100 HTTP_EMPTY_ARGS(send);
101 HTTP_BEGIN_ARGS(toString, 0)
102 HTTP_ARG_VAL(include_parent, 0)
103 HTTP_END_ARGS;
104
105 HTTP_EMPTY_ARGS(toMessageTypeObject);
106
107 HTTP_EMPTY_ARGS(count);
108
109 HTTP_EMPTY_ARGS(serialize);
110 HTTP_BEGIN_ARGS(unserialize, 1)
111 HTTP_ARG_VAL(serialized, 0)
112 HTTP_END_ARGS;
113
114 HTTP_EMPTY_ARGS(rewind);
115 HTTP_EMPTY_ARGS(valid);
116 HTTP_EMPTY_ARGS(key);
117 HTTP_EMPTY_ARGS(current);
118 HTTP_EMPTY_ARGS(next);
119
120 HTTP_EMPTY_ARGS(detach);
121 HTTP_BEGIN_ARGS(prepend, 1)
122 HTTP_ARG_OBJ(HttpMessage, message, 0)
123 HTTP_END_ARGS;
124 HTTP_EMPTY_ARGS(reverse);
125
126 #define http_message_object_read_prop _http_message_object_read_prop
127 static zval *_http_message_object_read_prop(zval *object, zval *member, int type TSRMLS_DC);
128 #define http_message_object_write_prop _http_message_object_write_prop
129 static void _http_message_object_write_prop(zval *object, zval *member, zval *value TSRMLS_DC);
130 #define http_message_object_get_props _http_message_object_get_props
131 static HashTable *_http_message_object_get_props(zval *object TSRMLS_DC);
132
133 #define OBJ_PROP_CE http_message_object_ce
134 zend_class_entry *http_message_object_ce;
135 zend_function_entry http_message_object_fe[] = {
136 HTTP_MESSAGE_ME(__construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
137 HTTP_MESSAGE_ME(getBody, ZEND_ACC_PUBLIC)
138 HTTP_MESSAGE_ME(setBody, ZEND_ACC_PUBLIC)
139 HTTP_MESSAGE_ME(getHeaders, ZEND_ACC_PUBLIC)
140 HTTP_MESSAGE_ME(setHeaders, ZEND_ACC_PUBLIC)
141 HTTP_MESSAGE_ME(addHeaders, ZEND_ACC_PUBLIC)
142 HTTP_MESSAGE_ME(getType, ZEND_ACC_PUBLIC)
143 HTTP_MESSAGE_ME(setType, ZEND_ACC_PUBLIC)
144 HTTP_MESSAGE_ME(getResponseCode, ZEND_ACC_PUBLIC)
145 HTTP_MESSAGE_ME(setResponseCode, ZEND_ACC_PUBLIC)
146 HTTP_MESSAGE_ME(getResponseStatus, ZEND_ACC_PUBLIC)
147 HTTP_MESSAGE_ME(setResponseStatus, ZEND_ACC_PUBLIC)
148 HTTP_MESSAGE_ME(getRequestMethod, ZEND_ACC_PUBLIC)
149 HTTP_MESSAGE_ME(setRequestMethod, ZEND_ACC_PUBLIC)
150 HTTP_MESSAGE_ME(getRequestUrl, ZEND_ACC_PUBLIC)
151 HTTP_MESSAGE_ME(setRequestUrl, ZEND_ACC_PUBLIC)
152 HTTP_MESSAGE_ME(getHttpVersion, ZEND_ACC_PUBLIC)
153 HTTP_MESSAGE_ME(setHttpVersion, ZEND_ACC_PUBLIC)
154 HTTP_MESSAGE_ME(getParentMessage, ZEND_ACC_PUBLIC)
155 HTTP_MESSAGE_ME(send, ZEND_ACC_PUBLIC)
156 HTTP_MESSAGE_ME(toString, ZEND_ACC_PUBLIC)
157 HTTP_MESSAGE_ME(toMessageTypeObject, ZEND_ACC_PUBLIC)
158
159 /* implements Countable */
160 HTTP_MESSAGE_ME(count, ZEND_ACC_PUBLIC)
161
162 /* implements Serializable */
163 HTTP_MESSAGE_ME(serialize, ZEND_ACC_PUBLIC)
164 HTTP_MESSAGE_ME(unserialize, ZEND_ACC_PUBLIC)
165
166 /* implements Iterator */
167 HTTP_MESSAGE_ME(rewind, ZEND_ACC_PUBLIC)
168 HTTP_MESSAGE_ME(valid, ZEND_ACC_PUBLIC)
169 HTTP_MESSAGE_ME(current, ZEND_ACC_PUBLIC)
170 HTTP_MESSAGE_ME(key, ZEND_ACC_PUBLIC)
171 HTTP_MESSAGE_ME(next, ZEND_ACC_PUBLIC)
172
173 ZEND_MALIAS(HttpMessage, __toString, toString, HTTP_ARGS(HttpMessage, toString), ZEND_ACC_PUBLIC)
174
175 HTTP_MESSAGE_ME(fromString, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
176
177 HTTP_MESSAGE_ME(detach, ZEND_ACC_PUBLIC)
178 HTTP_MESSAGE_ME(prepend, ZEND_ACC_PUBLIC)
179 HTTP_MESSAGE_ME(reverse, ZEND_ACC_PUBLIC)
180
181 EMPTY_FUNCTION_ENTRY
182 };
183 static zend_object_handlers http_message_object_handlers;
184
185 PHP_MINIT_FUNCTION(http_message_object)
186 {
187 HTTP_REGISTER_CLASS_EX(HttpMessage, http_message_object, NULL, 0);
188
189 #ifndef WONKY
190 # ifdef HAVE_SPL
191 zend_class_implements(http_message_object_ce TSRMLS_CC, 3, spl_ce_Countable, zend_ce_serializable, zend_ce_iterator);
192 # else
193 zend_class_implements(http_message_object_ce TSRMLS_CC, 2, zend_ce_serializable, zend_ce_iterator);
194 # endif
195 #else
196 zend_class_implements(http_message_object_ce TSRMLS_CC, 1, zend_ce_iterator);
197 #endif
198
199 http_message_object_handlers.clone_obj = _http_message_object_clone_obj;
200 http_message_object_handlers.read_property = http_message_object_read_prop;
201 http_message_object_handlers.write_property = http_message_object_write_prop;
202 http_message_object_handlers.get_properties = http_message_object_get_props;
203 http_message_object_handlers.get_property_ptr_ptr = NULL;
204
205 DCL_PROP(PROTECTED, long, type, HTTP_MSG_NONE);
206 DCL_PROP(PROTECTED, string, body, "");
207 DCL_PROP(PROTECTED, string, requestMethod, "");
208 DCL_PROP(PROTECTED, string, requestUrl, "");
209 DCL_PROP(PROTECTED, string, responseStatus, "");
210 DCL_PROP(PROTECTED, long, responseCode, 0);
211 DCL_PROP_N(PROTECTED, httpVersion);
212 DCL_PROP_N(PROTECTED, headers);
213 DCL_PROP_N(PROTECTED, parentMessage);
214
215 #ifndef WONKY
216 DCL_CONST(long, "TYPE_NONE", HTTP_MSG_NONE);
217 DCL_CONST(long, "TYPE_REQUEST", HTTP_MSG_REQUEST);
218 DCL_CONST(long, "TYPE_RESPONSE", HTTP_MSG_RESPONSE);
219 #endif
220
221 HTTP_LONG_CONSTANT("HTTP_MSG_NONE", HTTP_MSG_NONE);
222 HTTP_LONG_CONSTANT("HTTP_MSG_REQUEST", HTTP_MSG_REQUEST);
223 HTTP_LONG_CONSTANT("HTTP_MSG_RESPONSE", HTTP_MSG_RESPONSE);
224
225 return SUCCESS;
226 }
227
228 void _http_message_object_reverse(zval *this_ptr, zval *return_value TSRMLS_DC)
229 {
230 int i;
231 getObject(http_message_object, obj);
232
233 /* count */
234 http_message_count(i, obj->message);
235
236 if (i > 1) {
237 zval o;
238 zend_object_value *ovalues = NULL;
239 http_message_object **objects = NULL;
240 int last = i - 1;
241
242 objects = ecalloc(i, sizeof(http_message_object *));
243 ovalues = ecalloc(i, sizeof(zend_object_value));
244
245 /* we are the first message */
246 objects[0] = obj;
247 ovalues[0] = getThis()->value.obj;
248
249 /* fetch parents */
250 INIT_PZVAL(&o);
251 o.type = IS_OBJECT;
252 for (i = 1; obj->parent.handle; ++i) {
253 o.value.obj = obj->parent;
254 ovalues[i] = o.value.obj;
255 objects[i] = obj = zend_object_store_get_object(&o TSRMLS_CC);
256 }
257
258 /* reorder parents */
259 for (last = --i; i; --i) {
260 objects[i]->message->parent = objects[i-1]->message;
261 objects[i]->parent = ovalues[i-1];
262 }
263 objects[0]->message->parent = NULL;
264 objects[0]->parent.handle = 0;
265 objects[0]->parent.handlers = NULL;
266
267 /* add ref (why?) */
268 Z_OBJ_ADDREF_P(getThis());
269 RETVAL_OBJVAL(ovalues[last], 1);
270
271 efree(objects);
272 efree(ovalues);
273 } else {
274 RETURN_ZVAL(getThis(), 1, 0);
275 }
276 }
277
278 void _http_message_object_prepend_ex(zval *this_ptr, zval *prepend, zend_bool top TSRMLS_DC)
279 {
280 zval m;
281 http_message *save_parent_msg = NULL;
282 zend_object_value save_parent_obj = {0, NULL};
283 getObject(http_message_object, obj);
284 getObjectEx(http_message_object, prepend_obj, prepend);
285
286 INIT_PZVAL(&m);
287 m.type = IS_OBJECT;
288
289 if (!top) {
290 save_parent_obj = obj->parent;
291 save_parent_msg = obj->message->parent;
292 } else {
293 /* iterate to the most parent object */
294 while (obj->parent.handle) {
295 m.value.obj = obj->parent;
296 obj = zend_object_store_get_object(&m TSRMLS_CC);
297 }
298 }
299
300 /* prepend */
301 obj->parent = prepend->value.obj;
302 obj->message->parent = prepend_obj->message;
303
304 /* add ref */
305 zend_objects_store_add_ref(prepend TSRMLS_CC);
306 while (prepend_obj->parent.handle) {
307 m.value.obj = prepend_obj->parent;
308 zend_objects_store_add_ref(&m TSRMLS_CC);
309 prepend_obj = zend_object_store_get_object(&m TSRMLS_CC);
310 }
311
312 if (!top) {
313 prepend_obj->parent = save_parent_obj;
314 prepend_obj->message->parent = save_parent_msg;
315 }
316 }
317
318 zend_object_value _http_message_object_new(zend_class_entry *ce TSRMLS_DC)
319 {
320 return http_message_object_new_ex(ce, NULL, NULL);
321 }
322
323 zend_object_value _http_message_object_new_ex(zend_class_entry *ce, http_message *msg, http_message_object **ptr TSRMLS_DC)
324 {
325 zend_object_value ov;
326 http_message_object *o;
327
328 o = ecalloc(1, sizeof(http_message_object));
329 o->zo.ce = ce;
330
331 if (ptr) {
332 *ptr = o;
333 }
334
335 if (msg) {
336 o->message = msg;
337 if (msg->parent) {
338 o->parent = http_message_object_new_ex(ce, msg->parent, NULL);
339 }
340 }
341
342 ALLOC_HASHTABLE(OBJ_PROP(o));
343 zend_hash_init(OBJ_PROP(o), 0, NULL, ZVAL_PTR_DTOR, 0);
344
345 ov.handle = putObject(http_message_object, o);
346 ov.handlers = &http_message_object_handlers;
347
348 return ov;
349 }
350
351 zend_object_value _http_message_object_clone_obj(zval *this_ptr TSRMLS_DC)
352 {
353 getObject(http_message_object, obj);
354 return http_message_object_new_ex(Z_OBJCE_P(this_ptr), http_message_dup(obj->message), NULL);
355 }
356
357 void _http_message_object_free(zend_object *object TSRMLS_DC)
358 {
359 http_message_object *o = (http_message_object *) object;
360
361 if (OBJ_PROP(o)) {
362 zend_hash_destroy(OBJ_PROP(o));
363 FREE_HASHTABLE(OBJ_PROP(o));
364 }
365 if (o->message) {
366 http_message_dtor(o->message);
367 efree(o->message);
368 }
369 if (o->parent.handle) {
370 zval p;
371
372 INIT_PZVAL(&p);
373 p.type = IS_OBJECT;
374 p.value.obj = o->parent;
375 zend_objects_store_del_ref(&p TSRMLS_CC);
376 }
377 efree(o);
378 }
379
380 static zval *_http_message_object_read_prop(zval *object, zval *member, int type TSRMLS_DC)
381 {
382 getObjectEx(http_message_object, obj, object);
383 http_message *msg = obj->message;
384 zval *return_value;
385 #ifdef WONKY
386 ulong h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member)+1);
387 #else
388 zend_property_info *pinfo = zend_get_property_info(obj->zo.ce, member, 1 TSRMLS_CC);
389
390 if (!pinfo || ACC_PROP_PUBLIC(pinfo->flags)) {
391 return zend_get_std_object_handlers()->read_property(object, member, type TSRMLS_CC);
392 }
393 #endif
394
395 if (type == BP_VAR_W) {
396 zend_error(E_ERROR, "Cannot access HttpMessage properties by reference or array key/index");
397 return NULL;
398 }
399
400 ALLOC_ZVAL(return_value);
401 return_value->refcount = 0;
402 return_value->is_ref = 0;
403
404 #ifdef WONKY
405 switch (h)
406 #else
407 switch (pinfo->h)
408 #endif
409 {
410 case HTTP_MSG_PROPHASH_TYPE:
411 case HTTP_MSG_CHILD_PROPHASH_TYPE:
412 RETVAL_LONG(msg->type);
413 break;
414
415 case HTTP_MSG_PROPHASH_HTTP_VERSION:
416 case HTTP_MSG_CHILD_PROPHASH_HTTP_VERSION:
417 RETVAL_DOUBLE(msg->http.version);
418 break;
419
420 case HTTP_MSG_PROPHASH_BODY:
421 case HTTP_MSG_CHILD_PROPHASH_BODY:
422 phpstr_fix(PHPSTR(msg));
423 RETVAL_PHPSTR(PHPSTR(msg), 0, 1);
424 break;
425
426 case HTTP_MSG_PROPHASH_HEADERS:
427 case HTTP_MSG_CHILD_PROPHASH_HEADERS:
428 array_init(return_value);
429 zend_hash_copy(Z_ARRVAL_P(return_value), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
430 break;
431
432 case HTTP_MSG_PROPHASH_PARENT_MESSAGE:
433 case HTTP_MSG_CHILD_PROPHASH_PARENT_MESSAGE:
434 if (msg->parent) {
435 RETVAL_OBJVAL(obj->parent, 1);
436 } else {
437 RETVAL_NULL();
438 }
439 break;
440
441 case HTTP_MSG_PROPHASH_REQUEST_METHOD:
442 case HTTP_MSG_CHILD_PROPHASH_REQUEST_METHOD:
443 if (HTTP_MSG_TYPE(REQUEST, msg) && msg->http.info.request.method) {
444 RETVAL_STRING(msg->http.info.request.method, 1);
445 } else {
446 RETVAL_NULL();
447 }
448 break;
449
450 case HTTP_MSG_PROPHASH_REQUEST_URL:
451 case HTTP_MSG_CHILD_PROPHASH_REQUEST_URL:
452 if (HTTP_MSG_TYPE(REQUEST, msg) && msg->http.info.request.url) {
453 RETVAL_STRING(msg->http.info.request.url, 1);
454 } else {
455 RETVAL_NULL();
456 }
457 break;
458
459 case HTTP_MSG_PROPHASH_RESPONSE_CODE:
460 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_CODE:
461 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
462 RETVAL_LONG(msg->http.info.response.code);
463 } else {
464 RETVAL_NULL();
465 }
466 break;
467
468 case HTTP_MSG_PROPHASH_RESPONSE_STATUS:
469 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_STATUS:
470 if (HTTP_MSG_TYPE(RESPONSE, msg) && msg->http.info.response.status) {
471 RETVAL_STRING(msg->http.info.response.status, 1);
472 } else {
473 RETVAL_NULL();
474 }
475 break;
476
477 default:
478 #ifdef WONKY
479 return zend_get_std_object_handlers()->read_property(object, member, type TSRMLS_CC);
480 #else
481 RETVAL_NULL();
482 #endif
483 }
484
485 return return_value;
486 }
487
488 static void _http_message_object_write_prop(zval *object, zval *member, zval *value TSRMLS_DC)
489 {
490 getObjectEx(http_message_object, obj, object);
491 http_message *msg = obj->message;
492 zval *cpy = NULL;
493 #ifdef WONKY
494 ulong h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1);
495 #else
496 zend_property_info *pinfo = zend_get_property_info(obj->zo.ce, member, 1 TSRMLS_CC);
497
498 if (!pinfo || ACC_PROP_PUBLIC(pinfo->flags)) {
499 zend_get_std_object_handlers()->write_property(object, member, value TSRMLS_CC);
500 return;
501 }
502 #endif
503
504 cpy = zval_copy(Z_TYPE_P(value), value);
505
506 #ifdef WONKY
507 switch (h)
508 #else
509 switch (pinfo->h)
510 #endif
511 {
512 case HTTP_MSG_PROPHASH_TYPE:
513 case HTTP_MSG_CHILD_PROPHASH_TYPE:
514 convert_to_long(cpy);
515 http_message_set_type(msg, Z_LVAL_P(cpy));
516 break;
517
518 case HTTP_MSG_PROPHASH_HTTP_VERSION:
519 case HTTP_MSG_CHILD_PROPHASH_HTTP_VERSION:
520 convert_to_double(cpy);
521 msg->http.version = Z_DVAL_P(cpy);
522 break;
523
524 case HTTP_MSG_PROPHASH_BODY:
525 case HTTP_MSG_CHILD_PROPHASH_BODY:
526 convert_to_string(cpy);
527 phpstr_dtor(PHPSTR(msg));
528 phpstr_from_string_ex(PHPSTR(msg), Z_STRVAL_P(cpy), Z_STRLEN_P(cpy));
529 break;
530
531 case HTTP_MSG_PROPHASH_HEADERS:
532 case HTTP_MSG_CHILD_PROPHASH_HEADERS:
533 convert_to_array(cpy);
534 zend_hash_clean(&msg->hdrs);
535 zend_hash_copy(&msg->hdrs, Z_ARRVAL_P(cpy), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
536 break;
537
538 case HTTP_MSG_PROPHASH_PARENT_MESSAGE:
539 case HTTP_MSG_CHILD_PROPHASH_PARENT_MESSAGE:
540 if (Z_TYPE_P(value) == IS_OBJECT && instanceof_function(Z_OBJCE_P(value), http_message_object_ce TSRMLS_CC)) {
541 if (msg->parent) {
542 zval tmp;
543 tmp.value.obj = obj->parent;
544 Z_OBJ_DELREF(tmp);
545 }
546 Z_OBJ_ADDREF_P(value);
547 obj->parent = value->value.obj;
548 }
549 break;
550
551 case HTTP_MSG_PROPHASH_REQUEST_METHOD:
552 case HTTP_MSG_CHILD_PROPHASH_REQUEST_METHOD:
553 if (HTTP_MSG_TYPE(REQUEST, msg)) {
554 convert_to_string(cpy);
555 STR_SET(msg->http.info.request.method, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
556 }
557 break;
558
559 case HTTP_MSG_PROPHASH_REQUEST_URL:
560 case HTTP_MSG_CHILD_PROPHASH_REQUEST_URL:
561 if (HTTP_MSG_TYPE(REQUEST, msg)) {
562 convert_to_string(cpy);
563 STR_SET(msg->http.info.request.url, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
564 }
565 break;
566
567 case HTTP_MSG_PROPHASH_RESPONSE_CODE:
568 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_CODE:
569 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
570 convert_to_long(cpy);
571 msg->http.info.response.code = Z_LVAL_P(cpy);
572 }
573 break;
574
575 case HTTP_MSG_PROPHASH_RESPONSE_STATUS:
576 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_STATUS:
577 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
578 convert_to_string(cpy);
579 STR_SET(msg->http.info.response.status, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
580 }
581 break;
582
583 default:
584 #ifdef WONKY
585 zend_get_std_object_handlers()->write_property(object, member, value TSRMLS_CC);
586 #endif
587 break;
588 }
589 zval_free(&cpy);
590 }
591
592 static HashTable *_http_message_object_get_props(zval *object TSRMLS_DC)
593 {
594 zval *headers;
595 getObjectEx(http_message_object, obj, object);
596 http_message *msg = obj->message;
597 HashTable *props = OBJ_PROP(obj);
598 zval array, *parent;
599
600 INIT_ZARR(array, props);
601
602 #define ASSOC_PROP(array, ptype, name, val) \
603 { \
604 char *m_prop_name; \
605 int m_prop_len; \
606 zend_mangle_property_name(&m_prop_name, &m_prop_len, "*", 1, name, lenof(name), 0); \
607 add_assoc_ ##ptype## _ex(&array, m_prop_name, sizeof(name)+3, val); \
608 efree(m_prop_name); \
609 }
610 #define ASSOC_STRING(array, name, val) ASSOC_STRINGL(array, name, val, strlen(val))
611 #define ASSOC_STRINGL(array, name, val, len) \
612 { \
613 char *m_prop_name; \
614 int m_prop_len; \
615 zend_mangle_property_name(&m_prop_name, &m_prop_len, "*", 1, name, lenof(name), 0); \
616 add_assoc_stringl_ex(&array, m_prop_name, sizeof(name)+3, val, len, 1); \
617 efree(m_prop_name); \
618 }
619
620 ASSOC_PROP(array, long, "type", msg->type);
621 ASSOC_PROP(array, double, "httpVersion", msg->http.version);
622
623 switch (msg->type) {
624 case HTTP_MSG_REQUEST:
625 ASSOC_PROP(array, long, "responseCode", 0);
626 ASSOC_STRINGL(array, "responseStatus", "", 0);
627 ASSOC_STRING(array, "requestMethod", msg->http.info.request.method);
628 ASSOC_STRING(array, "requestUrl", msg->http.info.request.url);
629 break;
630
631 case HTTP_MSG_RESPONSE:
632 ASSOC_PROP(array, long, "responseCode", msg->http.info.response.code);
633 ASSOC_STRING(array, "responseStatus", msg->http.info.response.status);
634 ASSOC_STRINGL(array, "requestMethod", "", 0);
635 ASSOC_STRINGL(array, "requestUrl", "", 0);
636 break;
637
638 case HTTP_MSG_NONE:
639 default:
640 ASSOC_PROP(array, long, "responseCode", 0);
641 ASSOC_STRINGL(array, "responseStatus", "", 0);
642 ASSOC_STRINGL(array, "requestMethod", "", 0);
643 ASSOC_STRINGL(array, "requestUrl", "", 0);
644 break;
645 }
646
647 MAKE_STD_ZVAL(headers);
648 array_init(headers);
649 zend_hash_copy(Z_ARRVAL_P(headers), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
650 ASSOC_PROP(array, zval, "headers", headers);
651 ASSOC_STRINGL(array, "body", PHPSTR_VAL(msg), PHPSTR_LEN(msg));
652
653 MAKE_STD_ZVAL(parent);
654 if (msg->parent) {
655 ZVAL_OBJVAL(parent, obj->parent, 1);
656 } else {
657 ZVAL_NULL(parent);
658 }
659 ASSOC_PROP(array, zval, "parentMessage", parent);
660
661 return OBJ_PROP(obj);
662 }
663
664 /* ### USERLAND ### */
665
666 /* {{{ proto void HttpMessage::__construct([string message])
667 *
668 * Instantiate a new HttpMessage object.
669 *
670 * Accepts an optional string parameter containing a single or several
671 * consecutive HTTP messages. The constructed object will actually
672 * represent the *last* message of the passed string. If there were
673 * prior messages, those can be accessed by HttpMessage::getParentMessage().
674 *
675 * Throws HttpMalformedHeaderException.
676 */
677 PHP_METHOD(HttpMessage, __construct)
678 {
679 int length = 0;
680 char *message = NULL;
681
682 getObject(http_message_object, obj);
683
684 SET_EH_THROW_HTTP();
685 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &message, &length) && message && length) {
686 http_message *msg = obj->message;
687
688 http_message_dtor(msg);
689 if ((obj->message = http_message_parse_ex(msg, message, length))) {
690 if (obj->message->parent) {
691 obj->parent = http_message_object_new_ex(Z_OBJCE_P(getThis()), obj->message->parent, NULL);
692 }
693 } else {
694 obj->message = http_message_init(msg);
695 }
696 }
697 if (!obj->message) {
698 obj->message = http_message_new();
699 }
700 SET_EH_NORMAL();
701 }
702 /* }}} */
703
704 /* {{{ proto static HttpMessage HttpMessage::fromString(string raw_message[, string class_name = "HttpMessage"])
705 *
706 * Create an HttpMessage object from a string. Kind of a static constructor.
707 *
708 * Expects a string parameter containing a single or several consecutive
709 * HTTP messages. Accepts an optional string parameter specifying the class to use.
710 *
711 * Returns an HttpMessage object on success or NULL on failure.
712 *
713 * Throws HttpMalformedHeadersException.
714 */
715 PHP_METHOD(HttpMessage, fromString)
716 {
717 char *string = NULL, *class_name = NULL;
718 int length = 0, class_length = 0;
719 http_message *msg = NULL;
720
721 RETVAL_NULL();
722
723 SET_EH_THROW_HTTP();
724 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &string, &length, &class_name, &class_length)) {
725 if ((msg = http_message_parse(string, length))) {
726 zend_class_entry *ce = http_message_object_ce;
727
728 if (class_name && *class_name) {
729 ce = zend_fetch_class(class_name, class_length, ZEND_FETCH_CLASS_DEFAULT TSRMLS_CC);
730 if (ce && !instanceof_function(ce, http_message_object_ce TSRMLS_CC)) {
731 http_error_ex(HE_WARNING, HTTP_E_RUNTIME, "Class %s does not extend HttpMessage", class_name);
732 ce = NULL;
733 }
734 }
735 if (ce) {
736 RETVAL_OBJVAL(http_message_object_new_ex(ce, msg, NULL), 0);
737 }
738 }
739 }
740 SET_EH_NORMAL();
741 }
742 /* }}} */
743
744 /* {{{ proto string HttpMessage::getBody()
745 *
746 * Get the body of the parsed HttpMessage.
747 *
748 * Returns the message body as string.
749 */
750 PHP_METHOD(HttpMessage, getBody)
751 {
752 NO_ARGS;
753
754 if (return_value_used) {
755 getObject(http_message_object, obj);
756 RETURN_PHPSTR(&obj->message->body, PHPSTR_FREE_NOT, 1);
757 }
758 }
759 /* }}} */
760
761 /* {{{ proto void HttpMessage::setBody(string body)
762 *
763 * Set the body of the HttpMessage.
764 * NOTE: Don't forget to update any headers accordingly.
765 *
766 * Expects a string parameter containing the new body of the message.
767 */
768 PHP_METHOD(HttpMessage, setBody)
769 {
770 char *body;
771 int len;
772 getObject(http_message_object, obj);
773
774 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &body, &len)) {
775 phpstr_dtor(PHPSTR(obj->message));
776 phpstr_from_string_ex(PHPSTR(obj->message), body, len);
777 }
778 }
779 /* }}} */
780
781 /* {{{ proto array HttpMessage::getHeaders()
782 *
783 * Get Message Headers.
784 *
785 * Returns an associative array containing the messages HTTP headers.
786 */
787 PHP_METHOD(HttpMessage, getHeaders)
788 {
789 NO_ARGS;
790
791 if (return_value_used) {
792 zval headers;
793 getObject(http_message_object, obj);
794
795 INIT_ZARR(headers, &obj->message->hdrs);
796 array_init(return_value);
797 array_copy(&headers, return_value);
798 }
799 }
800 /* }}} */
801
802 /* {{{ proto void HttpMessage::setHeaders(array headers)
803 *
804 * Sets new headers.
805 *
806 * Expects an associative array as parameter containing the new HTTP headers,
807 * which will replace *all* previous HTTP headers of the message.
808 */
809 PHP_METHOD(HttpMessage, setHeaders)
810 {
811 zval *new_headers, old_headers;
812 getObject(http_message_object, obj);
813
814 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &new_headers)) {
815 return;
816 }
817
818 zend_hash_clean(&obj->message->hdrs);
819 INIT_ZARR(old_headers, &obj->message->hdrs);
820 array_copy(new_headers, &old_headers);
821 }
822 /* }}} */
823
824 /* {{{ proto void HttpMessage::addHeaders(array headers[, bool append = false])
825 *
826 * Add headers. If append is true, headers with the same name will be separated, else overwritten.
827 *
828 * Expects an associative array as parameter containing the additional HTTP headers
829 * to add to the messages existing headers. If the optional bool parameter is true,
830 * and a header with the same name of one to add exists already, this respective
831 * header will be converted to an array containing both header values, otherwise
832 * it will be overwritten with the new header value.
833 */
834 PHP_METHOD(HttpMessage, addHeaders)
835 {
836 zval old_headers, *new_headers;
837 zend_bool append = 0;
838 getObject(http_message_object, obj);
839
840 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &new_headers, &append)) {
841 return;
842 }
843
844 INIT_ZARR(old_headers, &obj->message->hdrs);
845 if (append) {
846 array_append(new_headers, &old_headers);
847 } else {
848 array_merge(new_headers, &old_headers);
849 }
850 }
851 /* }}} */
852
853 /* {{{ proto int HttpMessage::getType()
854 *
855 * Get Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE)
856 *
857 * Returns the HttpMessage::TYPE.
858 */
859 PHP_METHOD(HttpMessage, getType)
860 {
861 NO_ARGS;
862
863 if (return_value_used) {
864 getObject(http_message_object, obj);
865 RETURN_LONG(obj->message->type);
866 }
867 }
868 /* }}} */
869
870 /* {{{ proto void HttpMessage::setType(int type)
871 *
872 * Set Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE)
873 *
874 * Expects an int parameter, the HttpMessage::TYPE.
875 */
876 PHP_METHOD(HttpMessage, setType)
877 {
878 long type;
879 getObject(http_message_object, obj);
880
881 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type)) {
882 return;
883 }
884 http_message_set_type(obj->message, type);
885 }
886 /* }}} */
887
888 /* {{{ proto int HttpMessage::getResponseCode()
889 *
890 * Get the Response Code of the Message.
891 *
892 * Returns the HTTP response code if the message is of type
893 * HttpMessage::TYPE_RESPONSE, else FALSE.
894 */
895 PHP_METHOD(HttpMessage, getResponseCode)
896 {
897 NO_ARGS;
898
899 if (return_value_used) {
900 getObject(http_message_object, obj);
901 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
902 RETURN_LONG(obj->message->http.info.response.code);
903 }
904 }
905 /* }}} */
906
907 /* {{{ proto bool HttpMessage::setResponseCode(int code)
908 *
909 * Set the response code of an HTTP Response Message.
910 *
911 * Expects an int parameter with the HTTP response code.
912 *
913 * Returns TRUE on success, or FALSE if the message is not of type
914 * HttpMessage::TYPE_RESPONSE or the response code is out of range (100-510).
915 */
916 PHP_METHOD(HttpMessage, setResponseCode)
917 {
918 long code;
919 getObject(http_message_object, obj);
920
921 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
922
923 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) {
924 RETURN_FALSE;
925 }
926 if (code < 100 || code > 510) {
927 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid response code (100-510): %ld", code);
928 RETURN_FALSE;
929 }
930
931 obj->message->http.info.response.code = code;
932 RETURN_TRUE;
933 }
934 /* }}} */
935
936 /* {{{ proto string HttpMessage::getResponseStatus()
937 *
938 * Get the Response Status of the message (i.e. the string following the response code).
939 *
940 * Returns the HTTP response status string if the message is of type
941 * HttpMessage::TYPE_RESPONSE, else FALSE.
942 */
943 PHP_METHOD(HttpMessage, getResponseStatus)
944 {
945 NO_ARGS;
946
947 if (return_value_used) {
948 getObject(http_message_object, obj);
949 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
950 RETURN_STRING(obj->message->http.info.response.status, 1);
951 }
952 }
953 /* }}} */
954
955 /* {{{ proto bool HttpMessage::setResponseStatus(string status)
956 *
957 * Set the Response Status of the HTTP message (i.e. the string following the response code).
958 *
959 * Expects a string parameter containing the response status text.
960 *
961 * Returns TRUE on success or FALSE if the message is not of type
962 * HttpMessage::TYPE_RESPONSE.
963 */
964 PHP_METHOD(HttpMessage, setResponseStatus)
965 {
966 char *status;
967 int status_len;
968 getObject(http_message_object, obj);
969
970 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
971
972 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &status, &status_len)) {
973 RETURN_FALSE;
974 }
975 STR_SET(obj->message->http.info.response.status, estrdup(status));
976 RETURN_TRUE;
977 }
978 /* }}} */
979
980 /* {{{ proto string HttpMessage::getRequestMethod()
981 *
982 * Get the Request Method of the Message.
983 *
984 * Returns the request method name on success, or FALSE if the message is
985 * not of type HttpMessage::TYPE_REQUEST.
986 */
987 PHP_METHOD(HttpMessage, getRequestMethod)
988 {
989 NO_ARGS;
990
991 if (return_value_used) {
992 getObject(http_message_object, obj);
993 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
994 RETURN_STRING(obj->message->http.info.request.method, 1);
995 }
996 }
997 /* }}} */
998
999 /* {{{ proto bool HttpMessage::setRequestMethod(string method)
1000 *
1001 * Set the Request Method of the HTTP Message.
1002 *
1003 * Expects a string parameter containing the request method name.
1004 *
1005 * Returns TRUE on success, or FALSE if the message is not of type
1006 * HttpMessage::TYPE_REQUEST or an invalid request method was supplied.
1007 */
1008 PHP_METHOD(HttpMessage, setRequestMethod)
1009 {
1010 char *method;
1011 int method_len;
1012 getObject(http_message_object, obj);
1013
1014 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
1015
1016 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len)) {
1017 RETURN_FALSE;
1018 }
1019 if (method_len < 1) {
1020 http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestMethod to an empty string");
1021 RETURN_FALSE;
1022 }
1023 if (!http_request_method_exists(1, 0, method)) {
1024 http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Unknown request method: %s", method);
1025 RETURN_FALSE;
1026 }
1027
1028 STR_SET(obj->message->http.info.request.method, estrndup(method, method_len));
1029 RETURN_TRUE;
1030 }
1031 /* }}} */
1032
1033 /* {{{ proto string HttpMessage::getRequestUrl()
1034 *
1035 * Get the Request URL of the Message.
1036 *
1037 * Returns the request url as string on success, or FALSE if the message
1038 * is not of type HttpMessage::TYPE_REQUEST.
1039 */
1040 PHP_METHOD(HttpMessage, getRequestUrl)
1041 {
1042 NO_ARGS;
1043
1044 if (return_value_used) {
1045 getObject(http_message_object, obj);
1046 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
1047 RETURN_STRING(obj->message->http.info.request.url, 1);
1048 }
1049 }
1050 /* }}} */
1051
1052 /* {{{ proto bool HttpMessage::setRequestUrl(string url)
1053 *
1054 * Set the Request URL of the HTTP Message.
1055 *
1056 * Expects a string parameters containing the request url.
1057 *
1058 * Returns TRUE on success, or FALSE if the message is not of type
1059 * HttpMessage::TYPE_REQUEST or supplied URL was empty.
1060 */
1061 PHP_METHOD(HttpMessage, setRequestUrl)
1062 {
1063 char *URI;
1064 int URIlen;
1065 getObject(http_message_object, obj);
1066
1067 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &URI, &URIlen)) {
1068 RETURN_FALSE;
1069 }
1070 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
1071 if (URIlen < 1) {
1072 http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestUrl to an empty string");
1073 RETURN_FALSE;
1074 }
1075
1076 STR_SET(obj->message->http.info.request.url, estrndup(URI, URIlen));
1077 RETURN_TRUE;
1078 }
1079 /* }}} */
1080
1081 /* {{{ proto string HttpMessage::getHttpVersion()
1082 *
1083 * Get the HTTP Protocol Version of the Message.
1084 *
1085 * Returns the HTTP protocol version as string.
1086 */
1087 PHP_METHOD(HttpMessage, getHttpVersion)
1088 {
1089 NO_ARGS;
1090
1091 if (return_value_used) {
1092 char ver[4] = {0};
1093 getObject(http_message_object, obj);
1094
1095 sprintf(ver, "%1.1lf", obj->message->http.version);
1096 RETURN_STRINGL(ver, 3, 1);
1097 }
1098 }
1099 /* }}} */
1100
1101 /* {{{ proto bool HttpMessage::setHttpVersion(string version)
1102 *
1103 * Set the HTTP Protocol version of the Message.
1104 *
1105 * Expects a string parameter containing the HTTP protocol version.
1106 *
1107 * Returns TRUE on success, or FALSE if supplied version is out of range (1.0/1.1).
1108 */
1109 PHP_METHOD(HttpMessage, setHttpVersion)
1110 {
1111 char v[4];
1112 zval *zv;
1113 getObject(http_message_object, obj);
1114
1115 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &zv)) {
1116 return;
1117 }
1118
1119 convert_to_double(zv);
1120 sprintf(v, "%1.1lf", Z_DVAL_P(zv));
1121 if (strcmp(v, "1.0") && strcmp(v, "1.1")) {
1122 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid HTTP protocol version (1.0 or 1.1): %s", v);
1123 RETURN_FALSE;
1124 }
1125
1126 obj->message->http.version = Z_DVAL_P(zv);
1127 RETURN_TRUE;
1128 }
1129 /* }}} */
1130
1131 /* {{{ proto HttpMessage HttpMessage::getParentMessage()
1132 *
1133 * Get parent Message.
1134 *
1135 * Returns the parent HttpMessage on success, or NULL if there's none.
1136 *
1137 * Throws HttpRuntimeException.
1138 */
1139 PHP_METHOD(HttpMessage, getParentMessage)
1140 {
1141 SET_EH_THROW_HTTP();
1142 NO_ARGS {
1143 getObject(http_message_object, obj);
1144
1145 if (obj->message->parent) {
1146 RETVAL_OBJVAL(obj->parent, 1);
1147 } else {
1148 http_error(HE_WARNING, HTTP_E_RUNTIME, "HttpMessage does not have a parent message");
1149 }
1150 }
1151 SET_EH_NORMAL();
1152 }
1153 /* }}} */
1154
1155 /* {{{ proto bool HttpMessage::send()
1156 *
1157 * Send the Message according to its type as Response or Request.
1158 * This provides limited functionality compared to HttpRequest and HttpResponse.
1159 *
1160 * Returns TRUE on success, or FALSE on failure.
1161 */
1162 PHP_METHOD(HttpMessage, send)
1163 {
1164 getObject(http_message_object, obj);
1165
1166 NO_ARGS;
1167
1168 RETURN_SUCCESS(http_message_send(obj->message));
1169 }
1170 /* }}} */
1171
1172 /* {{{ proto string HttpMessage::toString([bool include_parent = false])
1173 *
1174 * Get the string representation of the Message.
1175 *
1176 * Accepts a bool parameter which specifies whether the returned string
1177 * should also contain any parent messages.
1178 *
1179 * Returns the full message as string.
1180 */
1181 PHP_METHOD(HttpMessage, toString)
1182 {
1183 if (return_value_used) {
1184 char *string;
1185 size_t length;
1186 zend_bool include_parent = 0;
1187 getObject(http_message_object, obj);
1188
1189 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &include_parent)) {
1190 RETURN_FALSE;
1191 }
1192
1193 if (include_parent) {
1194 http_message_serialize(obj->message, &string, &length);
1195 } else {
1196 http_message_tostring(obj->message, &string, &length);
1197 }
1198 RETURN_STRINGL(string, length, 0);
1199 }
1200 }
1201 /* }}} */
1202
1203 /* {{{ proto HttpRequest|HttpResponse HttpMessage::toMessageTypeObject(void)
1204 *
1205 * Creates an object regarding to the type of the message.
1206 *
1207 * Returns either an HttpRequest or HttpResponse object on success, or NULL on failure.
1208 *
1209 * Throws HttpRuntimeException, HttpMessageTypeException, HttpHeaderException.
1210 */
1211 PHP_METHOD(HttpMessage, toMessageTypeObject)
1212 {
1213 SET_EH_THROW_HTTP();
1214
1215 NO_ARGS;
1216
1217 if (return_value_used) {
1218 getObject(http_message_object, obj);
1219
1220 switch (obj->message->type) {
1221 case HTTP_MSG_REQUEST:
1222 {
1223 #ifdef HTTP_HAVE_CURL
1224 int method;
1225 char *url;
1226 zval tmp, body, *array, *headers, *host = http_message_header(obj->message, "Host");
1227 php_url hurl, *purl = php_url_parse(obj->message->http.info.request.url);
1228
1229 MAKE_STD_ZVAL(array);
1230 array_init(array);
1231
1232 memset(&hurl, 0, sizeof(php_url));
1233 hurl.host = host ? Z_STRVAL_P(host) : NULL;
1234 http_build_url(HTTP_URL_REPLACE, purl, &hurl, NULL, &url, NULL);
1235 php_url_free(purl);
1236 add_assoc_string(array, "url", url, 0);
1237
1238 if ( (method = http_request_method_exists(1, 0, obj->message->http.info.request.method)) ||
1239 (method = http_request_method_register(obj->message->http.info.request.method, strlen(obj->message->http.info.request.method)))) {
1240 add_assoc_long(array, "method", method);
1241 }
1242
1243 if (10 == (int) (obj->message->http.version * 10)) {
1244 add_assoc_long(array, "protocol", CURL_HTTP_VERSION_1_0);
1245 }
1246
1247 MAKE_STD_ZVAL(headers);
1248 array_init(headers);
1249 INIT_ZARR(tmp, &obj->message->hdrs);
1250 array_copy(&tmp, headers);
1251 add_assoc_zval(array, "headers", headers);
1252
1253 object_init_ex(return_value, http_request_object_ce);
1254 zend_call_method_with_1_params(&return_value, http_request_object_ce, NULL, "setoptions", NULL, array);
1255 zval_ptr_dtor(&array);
1256
1257 INIT_PZVAL(&body);
1258 ZVAL_STRINGL(&body, PHPSTR_VAL(obj->message), PHPSTR_LEN(obj->message), 0);
1259 zend_call_method_with_1_params(&return_value, http_request_object_ce, NULL, "setrawpostdata", NULL, &body);
1260 #else
1261 http_error(HE_WARNING, HTTP_E_RUNTIME, "Cannot transform HttpMessage to HttpRequest (missing curl support)");
1262 #endif
1263 break;
1264 }
1265
1266 case HTTP_MSG_RESPONSE:
1267 {
1268 #ifndef WONKY
1269 HashPosition pos1, pos2;
1270 ulong idx;
1271 uint key_len;
1272 char *key = NULL;
1273 zval **header, **h, *body;
1274
1275 if (obj->message->http.info.response.code) {
1276 http_send_status(obj->message->http.info.response.code);
1277 }
1278
1279 object_init_ex(return_value, http_response_object_ce);
1280
1281 FOREACH_HASH_KEYLENVAL(pos1, &obj->message->hdrs, key, key_len, idx, header) {
1282 if (key) {
1283 zval zkey;
1284
1285 INIT_PZVAL(&zkey);
1286 ZVAL_STRINGL(&zkey, key, key_len, 0);
1287
1288 switch (Z_TYPE_PP(header)) {
1289 case IS_ARRAY:
1290 case IS_OBJECT:
1291 FOREACH_HASH_VAL(pos2, HASH_OF(*header), h) {
1292 ZVAL_ADDREF(*h);
1293 zend_call_method_with_2_params(&return_value, http_response_object_ce, NULL, "setheader", NULL, &zkey, *h);
1294 zval_ptr_dtor(h);
1295 }
1296 break;
1297
1298 default:
1299 ZVAL_ADDREF(*header);
1300 zend_call_method_with_2_params(&return_value, http_response_object_ce, NULL, "setheader", NULL, &zkey, *header);
1301 zval_ptr_dtor(header);
1302 break;
1303 }
1304 key = NULL;
1305 }
1306 }
1307
1308 MAKE_STD_ZVAL(body);
1309 ZVAL_STRINGL(body, PHPSTR_VAL(obj->message), PHPSTR_LEN(obj->message), 1);
1310 zend_call_method_with_1_params(&return_value, http_response_object_ce, NULL, "setdata", NULL, body);
1311 zval_ptr_dtor(&body);
1312 #else
1313 http_error(HE_WARNING, HTTP_E_RUNTIME, "Cannot transform HttpMessage to HttpResponse (need PHP 5.1+)");
1314 #endif
1315 break;
1316 }
1317
1318 default:
1319 http_error(HE_WARNING, HTTP_E_MESSAGE_TYPE, "HttpMessage is neither of type HttpMessage::TYPE_REQUEST nor HttpMessage::TYPE_RESPONSE");
1320 break;
1321 }
1322 }
1323 SET_EH_NORMAL();
1324 }
1325 /* }}} */
1326
1327 /* {{{ proto int HttpMessage::count()
1328 *
1329 * Implements Countable.
1330 *
1331 * Returns the number of parent messages + 1.
1332 */
1333 PHP_METHOD(HttpMessage, count)
1334 {
1335 NO_ARGS {
1336 long i;
1337 getObject(http_message_object, obj);
1338
1339 http_message_count(i, obj->message);
1340 RETURN_LONG(i);
1341 }
1342 }
1343 /* }}} */
1344
1345 /* {{{ proto string HttpMessage::serialize()
1346 *
1347 * Implements Serializable.
1348 *
1349 * Returns the serialized representation of the HttpMessage.
1350 */
1351 PHP_METHOD(HttpMessage, serialize)
1352 {
1353 NO_ARGS {
1354 char *string;
1355 size_t length;
1356 getObject(http_message_object, obj);
1357
1358 http_message_serialize(obj->message, &string, &length);
1359 RETURN_STRINGL(string, length, 0);
1360 }
1361 }
1362 /* }}} */
1363
1364 /* {{{ proto void HttpMessage::unserialize(string serialized)
1365 *
1366 * Implements Serializable.
1367 *
1368 * Re-constructs the HttpMessage based upon the serialized string.
1369 */
1370 PHP_METHOD(HttpMessage, unserialize)
1371 {
1372 int length;
1373 char *serialized;
1374 getObject(http_message_object, obj);
1375
1376 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &serialized, &length)) {
1377 http_message_dtor(obj->message);
1378 if (!http_message_parse_ex(obj->message, serialized, (size_t) length)) {
1379 http_error(HE_ERROR, HTTP_E_RUNTIME, "Could not unserialize HttpMessage");
1380 http_message_init(obj->message);
1381 }
1382 }
1383 }
1384 /* }}} */
1385
1386 /* {{{ proto HttpMessage HttpMessage::detach(void)
1387 *
1388 * Returns a clone of an HttpMessage object detached from any parent messages.
1389 */
1390 PHP_METHOD(HttpMessage, detach)
1391 {
1392 http_info info;
1393 http_message *msg;
1394 getObject(http_message_object, obj);
1395
1396 NO_ARGS;
1397
1398 info.type = obj->message->type;
1399 memcpy(&HTTP_INFO(&info), &HTTP_INFO(obj->message), sizeof(struct http_info));
1400
1401 msg = http_message_new();
1402 http_message_set_info(msg, &info);
1403
1404 zend_hash_copy(&msg->hdrs, &obj->message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
1405 phpstr_append(&msg->body, PHPSTR_VAL(obj->message), PHPSTR_LEN(obj->message));
1406
1407 RETVAL_OBJVAL(http_message_object_new_ex(Z_OBJCE_P(getThis()), msg, NULL), 0);
1408 }
1409 /* }}} */
1410
1411 /* {{{ proto void HttpMessage::prepend(HttpMessage message[, bool top = true])
1412 *
1413 * Prepends message(s) to the HTTP message.
1414 *
1415 * Expects an HttpMessage object as parameter.
1416 *
1417 * Throws HttpInvalidParamException if the message is located within the same message chain.
1418 */
1419 PHP_METHOD(HttpMessage, prepend)
1420 {
1421 zval *prepend;
1422 zend_bool top = 1;
1423
1424 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|b", &prepend, http_message_object_ce, &top)) {
1425 http_message *msg[2];
1426 getObject(http_message_object, obj);
1427 getObjectEx(http_message_object, prepend_obj, prepend);
1428
1429 /* safety check */
1430 for (msg[0] = obj->message; msg[0]; msg[0] = msg[0]->parent) {
1431 for (msg[1] = prepend_obj->message; msg[1]; msg[1] = msg[1]->parent) {
1432 if (msg[0] == msg[1]) {
1433 http_error(HE_THROW, HTTP_E_INVALID_PARAM, "Cannot prepend a message located within the same message chain");
1434 return;
1435 }
1436 }
1437 }
1438
1439 http_message_object_prepend_ex(getThis(), prepend, top);
1440 }
1441 }
1442 /* }}} */
1443
1444 /* {{{ proto HttpMessage HttpMessage::reverse()
1445 *
1446 * Reorders the message chain in reverse order.
1447 *
1448 * Returns the most parent HttpMessage object.
1449 */
1450 PHP_METHOD(HttpMessage, reverse)
1451 {
1452 NO_ARGS {
1453 http_message_object_reverse(getThis(), return_value);
1454 }
1455 }
1456 /* }}} */
1457
1458 /* {{{ proto void HttpMessage::rewind(void)
1459 *
1460 * Implements Iterator.
1461 */
1462 PHP_METHOD(HttpMessage, rewind)
1463 {
1464 NO_ARGS {
1465 getObject(http_message_object, obj);
1466
1467 if (obj->iterator) {
1468 zval_ptr_dtor(&obj->iterator);
1469 }
1470 ZVAL_ADDREF(getThis());
1471 obj->iterator = getThis();
1472 }
1473 }
1474 /* }}} */
1475
1476 /* {{{ proto bool HttpMessage::valid(void)
1477 *
1478 * Implements Iterator.
1479 */
1480 PHP_METHOD(HttpMessage, valid)
1481 {
1482 NO_ARGS {
1483 getObject(http_message_object, obj);
1484
1485 RETURN_BOOL(obj->iterator != NULL);
1486 }
1487 }
1488 /* }}} */
1489
1490 /* {{{ proto void HttpMessage::next(void)
1491 *
1492 * Implements Iterator.
1493 */
1494 PHP_METHOD(HttpMessage, next)
1495 {
1496 NO_ARGS {
1497 getObject(http_message_object, obj);
1498 getObjectEx(http_message_object, itr, obj->iterator);
1499
1500 if (itr && itr->parent.handle) {
1501 zval *old = obj->iterator;
1502 MAKE_STD_ZVAL(obj->iterator);
1503 ZVAL_OBJVAL(obj->iterator, itr->parent, 1);
1504 zval_ptr_dtor(&old);
1505 } else {
1506 zval_ptr_dtor(&obj->iterator);
1507 obj->iterator = NULL;
1508 }
1509 }
1510 }
1511 /* }}} */
1512
1513 /* {{{ proto int HttpMessage::key(void)
1514 *
1515 * Implements Iterator.
1516 */
1517 PHP_METHOD(HttpMessage, key)
1518 {
1519 NO_ARGS {
1520 getObject(http_message_object, obj);
1521
1522 RETURN_LONG(obj->iterator ? obj->iterator->value.obj.handle:0);
1523 }
1524 }
1525 /* }}} */
1526
1527 /* {{{ proto HttpMessage HttpMessage::current(void)
1528 *
1529 * Implements Iterator.
1530 */
1531 PHP_METHOD(HttpMessage, current)
1532 {
1533 NO_ARGS {
1534 getObject(http_message_object, obj);
1535
1536 if (obj->iterator) {
1537 RETURN_ZVAL(obj->iterator, 1, 0);
1538 }
1539 }
1540 }
1541 /* }}} */
1542
1543 #endif /* ZEND_ENGINE_2 */
1544
1545 /*
1546 * Local variables:
1547 * tab-width: 4
1548 * c-basic-offset: 4
1549 * End:
1550 * vim600: noet sw=4 ts=4 fdm=marker
1551 * vim<600: noet sw=4 ts=4
1552 */
1553