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