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