-fix Countable not available in WONKY
[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-2005, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
19 #include "php.h"
20
21 #ifdef ZEND_ENGINE_2
22
23 #include "php_http.h"
24 #include "php_http_api.h"
25 #include "php_http_std_defs.h"
26 #include "php_http_message_object.h"
27 #include "php_http_exception_object.h"
28
29 #include "phpstr/phpstr.h"
30 #include "missing.h"
31
32 #ifndef WONKY
33 # include "zend_interfaces.h"
34 # if defined(HAVE_SPL)
35 /* SPL doesn't install its headers */
36 extern PHPAPI zend_class_entry *spl_ce_Countable;
37 # endif
38 #endif
39
40 ZEND_EXTERN_MODULE_GLOBALS(http);
41
42 #define HTTP_BEGIN_ARGS(method, ret_ref, req_args) HTTP_BEGIN_ARGS_EX(HttpMessage, method, ret_ref, req_args)
43 #define HTTP_EMPTY_ARGS(method, ret_ref) HTTP_EMPTY_ARGS_EX(HttpMessage, method, ret_ref)
44 #define HTTP_MESSAGE_ME(method, visibility) PHP_ME(HttpMessage, method, HTTP_ARGS(HttpMessage, method), visibility)
45
46 HTTP_BEGIN_ARGS(__construct, 0, 0)
47 HTTP_ARG_VAL(message, 0)
48 HTTP_END_ARGS;
49
50 HTTP_BEGIN_ARGS(fromString, 1, 1)
51 HTTP_ARG_VAL(message, 0)
52 HTTP_END_ARGS;
53
54 HTTP_EMPTY_ARGS(getBody, 0);
55 HTTP_BEGIN_ARGS(setBody, 0, 1)
56 HTTP_ARG_VAL(body, 0)
57 HTTP_END_ARGS;
58
59 HTTP_EMPTY_ARGS(getHeaders, 0);
60 HTTP_BEGIN_ARGS(setHeaders, 0, 1)
61 HTTP_ARG_VAL(headers, 0)
62 HTTP_END_ARGS;
63
64 HTTP_BEGIN_ARGS(addHeaders, 0, 1)
65 HTTP_ARG_VAL(headers, 0)
66 HTTP_ARG_VAL(append, 0)
67 HTTP_END_ARGS;
68
69 HTTP_EMPTY_ARGS(getType, 0);
70 HTTP_BEGIN_ARGS(setType, 0, 1)
71 HTTP_ARG_VAL(type, 0)
72 HTTP_END_ARGS;
73
74 HTTP_EMPTY_ARGS(getResponseCode, 0);
75 HTTP_BEGIN_ARGS(setResponseCode, 0, 1)
76 HTTP_ARG_VAL(response_code, 0)
77 HTTP_END_ARGS;
78
79 HTTP_EMPTY_ARGS(getRequestMethod, 0);
80 HTTP_BEGIN_ARGS(setRequestMethod, 0, 1)
81 HTTP_ARG_VAL(request_method, 0)
82 HTTP_END_ARGS;
83
84 HTTP_EMPTY_ARGS(getRequestUri, 0);
85 HTTP_BEGIN_ARGS(setRequestUri, 0, 1)
86 HTTP_ARG_VAL(uri, 0)
87 HTTP_END_ARGS;
88
89 HTTP_EMPTY_ARGS(getHttpVersion, 0);
90 HTTP_BEGIN_ARGS(setHttpVersion, 0, 1)
91 HTTP_ARG_VAL(http_version, 0)
92 HTTP_END_ARGS;
93
94 HTTP_EMPTY_ARGS(getParentMessage, 1);
95 HTTP_EMPTY_ARGS(send, 0);
96 HTTP_BEGIN_ARGS(toString, 0, 0)
97 HTTP_ARG_VAL(include_parent, 0)
98 HTTP_END_ARGS;
99
100 HTTP_EMPTY_ARGS(count, 0);
101
102 HTTP_EMPTY_ARGS(serialize, 0);
103 HTTP_BEGIN_ARGS(unserialize, 0, 1)
104 HTTP_ARG_VAL(serialized, 0)
105 HTTP_END_ARGS;
106
107 #define http_message_object_declare_default_properties() _http_message_object_declare_default_properties(TSRMLS_C)
108 static inline void _http_message_object_declare_default_properties(TSRMLS_D);
109 #define http_message_object_read_prop _http_message_object_read_prop
110 static zval *_http_message_object_read_prop(zval *object, zval *member, int type TSRMLS_DC);
111 #define http_message_object_write_prop _http_message_object_write_prop
112 static void _http_message_object_write_prop(zval *object, zval *member, zval *value TSRMLS_DC);
113 #define http_message_object_get_props _http_message_object_get_props
114 static HashTable *_http_message_object_get_props(zval *object TSRMLS_DC);
115
116 zend_class_entry *http_message_object_ce;
117 zend_function_entry http_message_object_fe[] = {
118 HTTP_MESSAGE_ME(__construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
119 HTTP_MESSAGE_ME(getBody, ZEND_ACC_PUBLIC)
120 HTTP_MESSAGE_ME(setBody, ZEND_ACC_PUBLIC)
121 HTTP_MESSAGE_ME(getHeaders, ZEND_ACC_PUBLIC)
122 HTTP_MESSAGE_ME(setHeaders, ZEND_ACC_PUBLIC)
123 HTTP_MESSAGE_ME(addHeaders, ZEND_ACC_PUBLIC)
124 HTTP_MESSAGE_ME(getType, ZEND_ACC_PUBLIC)
125 HTTP_MESSAGE_ME(setType, ZEND_ACC_PUBLIC)
126 HTTP_MESSAGE_ME(getResponseCode, ZEND_ACC_PUBLIC)
127 HTTP_MESSAGE_ME(setResponseCode, ZEND_ACC_PUBLIC)
128 HTTP_MESSAGE_ME(getRequestMethod, ZEND_ACC_PUBLIC)
129 HTTP_MESSAGE_ME(setRequestMethod, ZEND_ACC_PUBLIC)
130 HTTP_MESSAGE_ME(getRequestUri, ZEND_ACC_PUBLIC)
131 HTTP_MESSAGE_ME(setRequestUri, ZEND_ACC_PUBLIC)
132 HTTP_MESSAGE_ME(getHttpVersion, ZEND_ACC_PUBLIC)
133 HTTP_MESSAGE_ME(setHttpVersion, ZEND_ACC_PUBLIC)
134 HTTP_MESSAGE_ME(getParentMessage, ZEND_ACC_PUBLIC)
135 HTTP_MESSAGE_ME(send, ZEND_ACC_PUBLIC)
136 HTTP_MESSAGE_ME(toString, ZEND_ACC_PUBLIC)
137
138 /* implements Countable */
139 HTTP_MESSAGE_ME(count, ZEND_ACC_PUBLIC)
140
141 /* implements Serializable */
142 HTTP_MESSAGE_ME(serialize, ZEND_ACC_PUBLIC)
143 HTTP_MESSAGE_ME(unserialize, ZEND_ACC_PUBLIC)
144
145 ZEND_MALIAS(HttpMessage, __toString, toString, HTTP_ARGS(HttpMessage, toString), ZEND_ACC_PUBLIC)
146
147 HTTP_MESSAGE_ME(fromString, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
148
149 EMPTY_FUNCTION_ENTRY
150 };
151 static zend_object_handlers http_message_object_handlers;
152
153 PHP_MINIT_FUNCTION(http_message_object)
154 {
155 HTTP_REGISTER_CLASS_EX(HttpMessage, http_message_object, NULL, 0);
156 #ifndef WONKY
157 # ifdef HAVE_SPL
158 zend_class_implements(http_message_object_ce TSRMLS_CC, 2, spl_ce_Countable, zend_ce_serializable);
159 # else
160 zend_class_implements(http_message_object_ce TSRMLS_CC, 1, zend_ce_serializable);
161 # endif
162 #endif
163
164 HTTP_LONG_CONSTANT("HTTP_MSG_NONE", HTTP_MSG_NONE);
165 HTTP_LONG_CONSTANT("HTTP_MSG_REQUEST", HTTP_MSG_REQUEST);
166 HTTP_LONG_CONSTANT("HTTP_MSG_RESPONSE", HTTP_MSG_RESPONSE);
167
168 http_message_object_handlers.clone_obj = _http_message_object_clone_obj;
169 http_message_object_handlers.read_property = http_message_object_read_prop;
170 http_message_object_handlers.write_property = http_message_object_write_prop;
171 http_message_object_handlers.get_properties = http_message_object_get_props;
172 http_message_object_handlers.get_property_ptr_ptr = NULL;
173
174 return SUCCESS;
175 }
176
177 zend_object_value _http_message_object_new(zend_class_entry *ce TSRMLS_DC)
178 {
179 return http_message_object_new_ex(ce, NULL, NULL);
180 }
181
182 zend_object_value _http_message_object_new_ex(zend_class_entry *ce, http_message *msg, http_message_object **ptr TSRMLS_DC)
183 {
184 zend_object_value ov;
185 http_message_object *o;
186
187 o = ecalloc(1, sizeof(http_message_object));
188 o->zo.ce = ce;
189
190 if (ptr) {
191 *ptr = o;
192 }
193
194 if (msg) {
195 o->message = msg;
196 if (msg->parent) {
197 o->parent = http_message_object_new_ex(ce, msg->parent, NULL);
198 }
199 }
200
201 ALLOC_HASHTABLE(OBJ_PROP(o));
202 zend_hash_init(OBJ_PROP(o), 0, NULL, ZVAL_PTR_DTOR, 0);
203
204 ov.handle = putObject(http_message_object, o);
205 ov.handlers = &http_message_object_handlers;
206
207 return ov;
208 }
209
210 zend_object_value _http_message_object_clone_obj(zval *this_ptr TSRMLS_DC)
211 {
212 getObject(http_message_object, obj);
213 return http_message_object_new_ex(Z_OBJCE_P(this_ptr), http_message_dup(obj->message), NULL);
214 }
215
216 static inline void _http_message_object_declare_default_properties(TSRMLS_D)
217 {
218 zend_class_entry *ce = http_message_object_ce;
219
220 #ifndef WONKY
221 DCL_CONST(long, "TYPE_NONE", HTTP_MSG_NONE);
222 DCL_CONST(long, "TYPE_REQUEST", HTTP_MSG_REQUEST);
223 DCL_CONST(long, "TYPE_RESPONSE", HTTP_MSG_RESPONSE);
224 #endif
225
226 DCL_PROP(PROTECTED, long, type, HTTP_MSG_NONE);
227 DCL_PROP(PROTECTED, string, body, "");
228 DCL_PROP(PROTECTED, string, requestMethod, "");
229 DCL_PROP(PROTECTED, string, requestUri, "");
230 DCL_PROP(PROTECTED, long, responseCode, 0);
231 DCL_PROP_N(PROTECTED, httpVersion);
232 DCL_PROP_N(PROTECTED, headers);
233 DCL_PROP_N(PROTECTED, parentMessage);
234 }
235
236 void _http_message_object_free(zend_object *object TSRMLS_DC)
237 {
238 http_message_object *o = (http_message_object *) object;
239
240 if (OBJ_PROP(o)) {
241 zend_hash_destroy(OBJ_PROP(o));
242 FREE_HASHTABLE(OBJ_PROP(o));
243 }
244 if (o->message) {
245 http_message_dtor(o->message);
246 efree(o->message);
247 }
248 efree(o);
249 }
250
251 static zval *_http_message_object_read_prop(zval *object, zval *member, int type TSRMLS_DC)
252 {
253 getObjectEx(http_message_object, obj, object);
254 http_message *msg = obj->message;
255 zval *return_value;
256 #ifdef WONKY
257 ulong h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member)+1);
258 #else
259 zend_property_info *pinfo = zend_get_property_info(obj->zo.ce, member, 1 TSRMLS_CC);
260
261 if (!pinfo || ACC_PROP_PUBLIC(pinfo->flags)) {
262 return zend_get_std_object_handlers()->read_property(object, member, type TSRMLS_CC);
263 }
264 #endif
265
266 if (type == BP_VAR_W) {
267 zend_error(E_ERROR, "Cannot access HttpMessage properties by reference or array key/index");
268 return NULL;
269 }
270
271 ALLOC_ZVAL(return_value);
272 return_value->refcount = 0;
273 return_value->is_ref = 0;
274
275 #ifdef WONKY
276 switch (h)
277 #else
278 switch (pinfo->h)
279 #endif
280 {
281 case HTTP_MSG_PROPHASH_TYPE:
282 case HTTP_MSG_CHILD_PROPHASH_TYPE:
283 RETVAL_LONG(msg->type);
284 break;
285
286 case HTTP_MSG_PROPHASH_HTTP_VERSION:
287 case HTTP_MSG_CHILD_PROPHASH_HTTP_VERSION:
288 RETVAL_DOUBLE(msg->http.version);
289 break;
290
291 case HTTP_MSG_PROPHASH_BODY:
292 case HTTP_MSG_CHILD_PROPHASH_BODY:
293 phpstr_fix(PHPSTR(msg));
294 RETVAL_PHPSTR(PHPSTR(msg), 0, 1);
295 break;
296
297 case HTTP_MSG_PROPHASH_HEADERS:
298 case HTTP_MSG_CHILD_PROPHASH_HEADERS:
299 array_init(return_value);
300 zend_hash_copy(Z_ARRVAL_P(return_value), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
301 break;
302
303 case HTTP_MSG_PROPHASH_PARENT_MESSAGE:
304 case HTTP_MSG_CHILD_PROPHASH_PARENT_MESSAGE:
305 if (msg->parent) {
306 RETVAL_OBJVAL(obj->parent);
307 } else {
308 RETVAL_NULL();
309 }
310 break;
311
312 case HTTP_MSG_PROPHASH_REQUEST_METHOD:
313 case HTTP_MSG_CHILD_PROPHASH_REQUEST_METHOD:
314 if (HTTP_MSG_TYPE(REQUEST, msg) && msg->http.info.request.method) {
315 RETVAL_STRING(msg->http.info.request.method, 1);
316 } else {
317 RETVAL_NULL();
318 }
319 break;
320
321 case HTTP_MSG_PROPHASH_REQUEST_URI:
322 case HTTP_MSG_CHILD_PROPHASH_REQUEST_URI:
323 if (HTTP_MSG_TYPE(REQUEST, msg) && msg->http.info.request.URI) {
324 RETVAL_STRING(msg->http.info.request.URI, 1);
325 } else {
326 RETVAL_NULL();
327 }
328 break;
329
330 case HTTP_MSG_PROPHASH_RESPONSE_CODE:
331 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_CODE:
332 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
333 RETVAL_LONG(msg->http.info.response.code);
334 } else {
335 RETVAL_NULL();
336 }
337 break;
338
339 case HTTP_MSG_PROPHASH_RESPONSE_STATUS:
340 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_STATUS:
341 if (HTTP_MSG_TYPE(RESPONSE, msg) && msg->http.info.response.status) {
342 RETVAL_STRING(msg->http.info.response.status, 1);
343 } else {
344 RETVAL_NULL();
345 }
346 break;
347
348 default:
349 #ifdef WONKY
350 return zend_get_std_object_handlers()->read_property(object, member, type TSRMLS_CC);
351 #else
352 RETVAL_NULL();
353 #endif
354 break;
355 }
356
357 return return_value;
358 }
359
360 static void _http_message_object_write_prop(zval *object, zval *member, zval *value TSRMLS_DC)
361 {
362 getObjectEx(http_message_object, obj, object);
363 http_message *msg = obj->message;
364 zval *cpy = NULL;
365 #ifdef WONKY
366 ulong h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1);
367 #else
368 zend_property_info *pinfo = zend_get_property_info(obj->zo.ce, member, 1 TSRMLS_CC);
369
370 if (!pinfo || ACC_PROP_PUBLIC(pinfo->flags)) {
371 zend_get_std_object_handlers()->write_property(object, member, value TSRMLS_CC);
372 return;
373 }
374 #endif
375
376 ALLOC_ZVAL(cpy);
377 *cpy = *value;
378 zval_copy_ctor(cpy);
379 INIT_PZVAL(cpy);
380
381 #ifdef WONKY
382 switch (h)
383 #else
384 switch (pinfo->h)
385 #endif
386 {
387 case HTTP_MSG_PROPHASH_TYPE:
388 case HTTP_MSG_CHILD_PROPHASH_TYPE:
389 convert_to_long(cpy);
390 http_message_set_type(msg, Z_LVAL_P(cpy));
391 break;
392
393 case HTTP_MSG_PROPHASH_HTTP_VERSION:
394 case HTTP_MSG_CHILD_PROPHASH_HTTP_VERSION:
395 convert_to_double(cpy);
396 msg->http.version = Z_DVAL_P(cpy);
397 break;
398
399 case HTTP_MSG_PROPHASH_BODY:
400 case HTTP_MSG_CHILD_PROPHASH_BODY:
401 convert_to_string(cpy);
402 phpstr_dtor(PHPSTR(msg));
403 phpstr_from_string_ex(PHPSTR(msg), Z_STRVAL_P(cpy), Z_STRLEN_P(cpy));
404 break;
405
406 case HTTP_MSG_PROPHASH_HEADERS:
407 case HTTP_MSG_CHILD_PROPHASH_HEADERS:
408 convert_to_array(cpy);
409 zend_hash_clean(&msg->hdrs);
410 zend_hash_copy(&msg->hdrs, Z_ARRVAL_P(cpy), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
411 break;
412
413 case HTTP_MSG_PROPHASH_PARENT_MESSAGE:
414 case HTTP_MSG_CHILD_PROPHASH_PARENT_MESSAGE:
415 if (Z_TYPE_P(value) == IS_OBJECT && instanceof_function(Z_OBJCE_P(value), http_message_object_ce TSRMLS_CC)) {
416 if (msg->parent) {
417 zval tmp;
418 tmp.value.obj = obj->parent;
419 Z_OBJ_DELREF(tmp);
420 }
421 Z_OBJ_ADDREF_P(value);
422 obj->parent = value->value.obj;
423 }
424 break;
425
426 case HTTP_MSG_PROPHASH_REQUEST_METHOD:
427 case HTTP_MSG_CHILD_PROPHASH_REQUEST_METHOD:
428 if (HTTP_MSG_TYPE(REQUEST, msg)) {
429 convert_to_string(cpy);
430 STR_SET(msg->http.info.request.method, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
431 }
432 break;
433
434 case HTTP_MSG_PROPHASH_REQUEST_URI:
435 case HTTP_MSG_CHILD_PROPHASH_REQUEST_URI:
436 if (HTTP_MSG_TYPE(REQUEST, msg)) {
437 convert_to_string(cpy);
438 STR_SET(msg->http.info.request.URI, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
439 }
440 break;
441
442 case HTTP_MSG_PROPHASH_RESPONSE_CODE:
443 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_CODE:
444 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
445 convert_to_long(cpy);
446 msg->http.info.response.code = Z_LVAL_P(cpy);
447 }
448 break;
449
450 case HTTP_MSG_PROPHASH_RESPONSE_STATUS:
451 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_STATUS:
452 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
453 convert_to_string(cpy);
454 STR_SET(msg->http.info.response.status, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
455 }
456 break;
457
458 default:
459 #ifdef WONKY
460 zend_get_std_object_handlers()->write_property(object, member, value TSRMLS_CC);
461 #endif
462 break;
463 }
464 zval_ptr_dtor(&cpy);
465 }
466
467 static HashTable *_http_message_object_get_props(zval *object TSRMLS_DC)
468 {
469 zval *headers;
470 getObjectEx(http_message_object, obj, object);
471 http_message *msg = obj->message;
472 HashTable *props = OBJ_PROP(obj);
473 zval array;
474
475 INIT_ZARR(array, props);
476
477 #define ASSOC_PROP(array, ptype, name, val) \
478 { \
479 char *m_prop_name; \
480 int m_prop_len; \
481 zend_mangle_property_name(&m_prop_name, &m_prop_len, "*", 1, name, lenof(name), 0); \
482 add_assoc_ ##ptype## _ex(&array, m_prop_name, sizeof(name)+4, val); \
483 efree(m_prop_name); \
484 }
485 #define ASSOC_STRING(array, name, val) ASSOC_STRINGL(array, name, val, strlen(val))
486 #define ASSOC_STRINGL(array, name, val, len) \
487 { \
488 char *m_prop_name; \
489 int m_prop_len; \
490 zend_mangle_property_name(&m_prop_name, &m_prop_len, "*", 1, name, lenof(name), 0); \
491 add_assoc_stringl_ex(&array, m_prop_name, sizeof(name)+4, val, len, 1); \
492 efree(m_prop_name); \
493 }
494
495 ASSOC_PROP(array, long, "type", msg->type);
496 ASSOC_PROP(array, double, "httpVersion", msg->http.version);
497
498 switch (msg->type)
499 {
500 case HTTP_MSG_REQUEST:
501 ASSOC_PROP(array, long, "responseCode", 0);
502 ASSOC_STRINGL(array, "responseStatus", "", 0);
503 ASSOC_STRING(array, "requestMethod", msg->http.info.request.method);
504 ASSOC_STRING(array, "requestUri", msg->http.info.request.URI);
505 break;
506
507 case HTTP_MSG_RESPONSE:
508 ASSOC_PROP(array, long, "responseCode", msg->http.info.response.code);
509 ASSOC_STRING(array, "responseStatus", msg->http.info.response.status);
510 ASSOC_STRINGL(array, "requestMethod", "", 0);
511 ASSOC_STRINGL(array, "requestUri", "", 0);
512 break;
513
514 case HTTP_MSG_NONE:
515 default:
516 ASSOC_PROP(array, long, "responseCode", 0);
517 ASSOC_STRINGL(array, "responseStatus", "", 0);
518 ASSOC_STRINGL(array, "requestMethod", "", 0);
519 ASSOC_STRINGL(array, "requestUri", "", 0);
520 break;
521 }
522
523 MAKE_STD_ZVAL(headers);
524 array_init(headers);
525 zend_hash_copy(Z_ARRVAL_P(headers), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
526 ASSOC_PROP(array, zval, "headers", headers);
527 ASSOC_STRINGL(array, "body", PHPSTR_VAL(msg), PHPSTR_LEN(msg));
528
529 return OBJ_PROP(obj);
530 }
531
532 /* ### USERLAND ### */
533
534 /* {{{ proto void HttpMessage::__construct([string message])
535 *
536 * Instantiate a new HttpMessage object.
537 *
538 * Accepts an optional string parameter containing a single or several
539 * consecutive HTTP messages. The constructed object will actually
540 * represent the *last* message of the passed string. If there were
541 * prior messages, those can be accessed by HttpMessage::getParentMessage().
542 *
543 * Throws HttpMalformedHeaderException.
544 */
545 PHP_METHOD(HttpMessage, __construct)
546 {
547 int length = 0;
548 char *message = NULL;
549
550 getObject(http_message_object, obj);
551
552 SET_EH_THROW_HTTP();
553 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &message, &length) && message && length) {
554 http_message *msg = obj->message;
555
556 http_message_dtor(msg);
557 if ((obj->message = http_message_parse_ex(msg, message, length))) {
558 if (obj->message->parent) {
559 obj->parent = http_message_object_new_ex(Z_OBJCE_P(getThis()), obj->message->parent, NULL);
560 }
561 } else {
562 obj->message = http_message_init(msg);
563 }
564 }
565 if (!obj->message) {
566 obj->message = http_message_new();
567 }
568 SET_EH_NORMAL();
569 }
570 /* }}} */
571
572 /* {{{ proto static HttpMessage HttpMessage::fromString(string raw_message)
573 *
574 * Create an HttpMessage object from a string. Kind of a static constructor.
575 *
576 * Expects a string parameter containing a sinlge or several consecutive
577 * HTTP messages.
578 *
579 * Returns an HttpMessage object on success or NULL on failure.
580 *
581 * Throws HttpMalformedHeadersException.
582 */
583 PHP_METHOD(HttpMessage, fromString)
584 {
585 char *string = NULL;
586 int length = 0;
587 http_message *msg = NULL;
588
589 RETVAL_NULL();
590
591 SET_EH_THROW_HTTP();
592 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &string, &length)) {
593 if ((msg = http_message_parse(string, length))) {
594 ZVAL_OBJVAL(return_value, http_message_object_new_ex(http_message_object_ce, msg, NULL));
595 }
596 }
597 SET_EH_NORMAL();
598 }
599 /* }}} */
600
601 /* {{{ proto string HttpMessage::getBody()
602 *
603 * Get the body of the parsed HttpMessage.
604 *
605 * Returns the message body as string.
606 */
607 PHP_METHOD(HttpMessage, getBody)
608 {
609 NO_ARGS;
610
611 IF_RETVAL_USED {
612 getObject(http_message_object, obj);
613 RETURN_PHPSTR(&obj->message->body, PHPSTR_FREE_NOT, 1);
614 }
615 }
616 /* }}} */
617
618 /* {{{ proto void HttpMessage::setBody(string body)
619 *
620 * Set the body of the HttpMessage.
621 * NOTE: Don't forget to update any headers accordingly.
622 *
623 * Expects a string parameter containing the new body of the message.
624 */
625 PHP_METHOD(HttpMessage, setBody)
626 {
627 char *body;
628 int len;
629 getObject(http_message_object, obj);
630
631 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &body, &len)) {
632 phpstr_dtor(PHPSTR(obj->message));
633 phpstr_from_string_ex(PHPSTR(obj->message), body, len);
634 }
635 }
636 /* }}} */
637
638 /* {{{ proto array HttpMessage::getHeaders()
639 *
640 * Get Message Headers.
641 *
642 * Returns an associative array containing the messages HTTP headers.
643 */
644 PHP_METHOD(HttpMessage, getHeaders)
645 {
646 NO_ARGS;
647
648 IF_RETVAL_USED {
649 zval headers;
650 getObject(http_message_object, obj);
651
652 INIT_ZARR(headers, &obj->message->hdrs);
653 array_init(return_value);
654 array_copy(&headers, return_value);
655 }
656 }
657 /* }}} */
658
659 /* {{{ proto void HttpMessage::setHeaders(array headers)
660 *
661 * Sets new headers.
662 *
663 * Expects an associative array as parameter containing the new HTTP headers,
664 * which will replace *all* previous HTTP headers of the message.
665 */
666 PHP_METHOD(HttpMessage, setHeaders)
667 {
668 zval *new_headers, old_headers;
669 getObject(http_message_object, obj);
670
671 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &new_headers)) {
672 return;
673 }
674
675 zend_hash_clean(&obj->message->hdrs);
676 INIT_ZARR(old_headers, &obj->message->hdrs);
677 array_copy(new_headers, &old_headers);
678 }
679 /* }}} */
680
681 /* {{{ proto void HttpMessage::addHeaders(array headers[, bool append = false])
682 *
683 * Add headers. If append is true, headers with the same name will be separated, else overwritten.
684 *
685 * Expects an associative array as parameter containing the additional HTTP headers
686 * to add to the messages existing headers. If the optional bool parameter is true,
687 * and a header with the same name of one to add exists already, this respective
688 * header will be converted to an array containing both header values, otherwise
689 * it will be overwritten with the new header value.
690 */
691 PHP_METHOD(HttpMessage, addHeaders)
692 {
693 zval old_headers, *new_headers;
694 zend_bool append = 0;
695 getObject(http_message_object, obj);
696
697 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &new_headers, &append)) {
698 return;
699 }
700
701 INIT_ZARR(old_headers, &obj->message->hdrs);
702 if (append) {
703 array_append(new_headers, &old_headers);
704 } else {
705 array_merge(new_headers, &old_headers);
706 }
707 }
708 /* }}} */
709
710 /* {{{ proto int HttpMessage::getType()
711 *
712 * Get Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE)
713 *
714 * Returns the HttpMessage::TYPE.
715 */
716 PHP_METHOD(HttpMessage, getType)
717 {
718 NO_ARGS;
719
720 IF_RETVAL_USED {
721 getObject(http_message_object, obj);
722 RETURN_LONG(obj->message->type);
723 }
724 }
725 /* }}} */
726
727 /* {{{ proto void HttpMessage::setType(int type)
728 *
729 * Set Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE)
730 *
731 * Exptects an int parameter, the HttpMessage::TYPE.
732 */
733 PHP_METHOD(HttpMessage, setType)
734 {
735 long type;
736 getObject(http_message_object, obj);
737
738 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type)) {
739 return;
740 }
741 http_message_set_type(obj->message, type);
742 }
743 /* }}} */
744
745 /* {{{ proto int HttpMessage::getResponseCode()
746 *
747 * Get the Response Code of the Message.
748 *
749 * Returns the HTTP response code if the message is of type
750 * HttpMessage::TYPE_RESPONSE, else FALSE.
751 */
752 PHP_METHOD(HttpMessage, getResponseCode)
753 {
754 NO_ARGS;
755
756 IF_RETVAL_USED {
757 getObject(http_message_object, obj);
758 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
759 RETURN_LONG(obj->message->http.info.response.code);
760 }
761 }
762 /* }}} */
763
764 /* {{{ proto bool HttpMessage::setResponseCode(int code)
765 *
766 * Set the response code of an HTTP Response Message.
767 *
768 * Expects an int parameter with the HTTP response code.
769 *
770 * Returns TRUE on success, or FALSE if the message is not of type
771 * HttpMessage::TYPE_RESPONSE or the response code is out of range (100-510).
772 */
773 PHP_METHOD(HttpMessage, setResponseCode)
774 {
775 long code;
776 getObject(http_message_object, obj);
777
778 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
779
780 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) {
781 RETURN_FALSE;
782 }
783 if (code < 100 || code > 510) {
784 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid response code (100-510): %ld", code);
785 RETURN_FALSE;
786 }
787
788 obj->message->http.info.response.code = code;
789 RETURN_TRUE;
790 }
791 /* }}} */
792
793 /* {{{ proto string HttpMessage::getRequestMethod()
794 *
795 * Get the Request Method of the Message.
796 *
797 * Returns the request method name on success, or FALSE if the message is
798 * not of type HttpMessage::TYPE_REQUEST.
799 */
800 PHP_METHOD(HttpMessage, getRequestMethod)
801 {
802 NO_ARGS;
803
804 IF_RETVAL_USED {
805 getObject(http_message_object, obj);
806 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
807 RETURN_STRING(obj->message->http.info.request.method, 1);
808 }
809 }
810 /* }}} */
811
812 /* {{{ proto bool HttpMessage::setRequestMethod(string method)
813 *
814 * Set the Request Method of the HTTP Message.
815 *
816 * Expects a string parameter containing the request method name.
817 *
818 * Returns TRUE on success, or FALSE if the message is not of type
819 * HttpMessage::TYPE_REQUEST or an invalid request method was supplied.
820 */
821 PHP_METHOD(HttpMessage, setRequestMethod)
822 {
823 char *method;
824 int method_len;
825 getObject(http_message_object, obj);
826
827 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
828
829 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len)) {
830 RETURN_FALSE;
831 }
832 if (method_len < 1) {
833 http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestMethod to an empty string");
834 RETURN_FALSE;
835 }
836 if (SUCCESS != http_check_method(method)) {
837 http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Unkown request method: %s", method);
838 RETURN_FALSE;
839 }
840
841 STR_SET(obj->message->http.info.request.method, estrndup(method, method_len));
842 RETURN_TRUE;
843 }
844 /* }}} */
845
846 /* {{{ proto string HttpMessage::getRequestUri()
847 *
848 * Get the Request URI of the Message.
849 *
850 * Returns the request uri as string on success, or FALSE if the message
851 * is not of type HttpMessage::TYPE_REQUEST.
852 */
853 PHP_METHOD(HttpMessage, getRequestUri)
854 {
855 NO_ARGS;
856
857 IF_RETVAL_USED {
858 getObject(http_message_object, obj);
859 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
860 RETURN_STRING(obj->message->http.info.request.URI, 1);
861 }
862 }
863 /* }}} */
864
865 /* {{{ proto bool HttpMessage::setRequestUri(string URI)
866 *
867 * Set the Request URI of the HTTP Message.
868 *
869 * Expects a string parameters containing the request uri.
870 *
871 * Returns TRUE on success, or FALSE if the message is not of type
872 * HttpMessage::TYPE_REQUEST or supplied URI was empty.
873 */
874 PHP_METHOD(HttpMessage, setRequestUri)
875 {
876 char *URI;
877 int URIlen;
878 getObject(http_message_object, obj);
879
880 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &URI, &URIlen)) {
881 RETURN_FALSE;
882 }
883 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
884 if (URIlen < 1) {
885 http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestUri to an empty string");
886 RETURN_FALSE;
887 }
888
889 STR_SET(obj->message->http.info.request.URI, estrndup(URI, URIlen));
890 RETURN_TRUE;
891 }
892 /* }}} */
893
894 /* {{{ proto string HttpMessage::getHttpVersion()
895 *
896 * Get the HTTP Protocol Version of the Message.
897 *
898 * Returns the HTTP protocol version as string.
899 */
900 PHP_METHOD(HttpMessage, getHttpVersion)
901 {
902 NO_ARGS;
903
904 IF_RETVAL_USED {
905 char ver[4] = {0};
906 getObject(http_message_object, obj);
907
908 sprintf(ver, "%1.1lf", obj->message->http.version);
909 RETURN_STRINGL(ver, 3, 1);
910 }
911 }
912 /* }}} */
913
914 /* {{{ proto bool HttpMessage::setHttpVersion(string version)
915 *
916 * Set the HTTP Protocol version of the Message.
917 *
918 * Expects a string parameter containing the HTTP protocol version.
919 *
920 * Returns TRUE on success, or FALSE if supplied version is out of range (1.0/1.1).
921 */
922 PHP_METHOD(HttpMessage, setHttpVersion)
923 {
924 char v[4];
925 zval *zv;
926 getObject(http_message_object, obj);
927
928 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &zv)) {
929 return;
930 }
931
932 convert_to_double(zv);
933 sprintf(v, "%1.1lf", Z_DVAL_P(zv));
934 if (strcmp(v, "1.0") && strcmp(v, "1.1")) {
935 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid HTTP protocol version (1.0 or 1.1): %s", v);
936 RETURN_FALSE;
937 }
938
939 obj->message->http.version = Z_DVAL_P(zv);
940 RETURN_TRUE;
941 }
942 /* }}} */
943
944 /* {{{ proto HttpMessage HttpMessage::getParentMessage()
945 *
946 * Get parent Message.
947 *
948 * Returns the parent HttpMessage on success, or NULL if there's none.
949 */
950 PHP_METHOD(HttpMessage, getParentMessage)
951 {
952 NO_ARGS;
953
954 IF_RETVAL_USED {
955 getObject(http_message_object, obj);
956
957 if (obj->message->parent) {
958 RETVAL_OBJVAL(obj->parent);
959 } else {
960 RETVAL_NULL();
961 }
962 }
963 }
964 /* }}} */
965
966 /* {{{ proto bool HttpMessage::send()
967 *
968 * Send the Message according to its type as Response or Request.
969 * This provides limited functionality compared to HttpRequest and HttpResponse.
970 *
971 * Returns TRUE on success, or FALSE on failure.
972 */
973 PHP_METHOD(HttpMessage, send)
974 {
975 getObject(http_message_object, obj);
976
977 NO_ARGS;
978
979 RETURN_SUCCESS(http_message_send(obj->message));
980 }
981 /* }}} */
982
983 /* {{{ proto string HttpMessage::toString([bool include_parent = false])
984 *
985 * Get the string representation of the Message.
986 *
987 * Accepts a bool parameter which specifies whether the returned string
988 * should also contain any parent messages.
989 *
990 * Returns the full message as string.
991 */
992 PHP_METHOD(HttpMessage, toString)
993 {
994 IF_RETVAL_USED {
995 char *string;
996 size_t length;
997 zend_bool include_parent = 0;
998 getObject(http_message_object, obj);
999
1000 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &include_parent)) {
1001 RETURN_FALSE;
1002 }
1003
1004 if (include_parent) {
1005 http_message_serialize(obj->message, &string, &length);
1006 } else {
1007 http_message_tostring(obj->message, &string, &length);
1008 }
1009 RETURN_STRINGL(string, length, 0);
1010 }
1011 }
1012 /* }}} */
1013
1014 /* {{{ proto int HttpMessage::count()
1015 *
1016 * Implements Countable.
1017 *
1018 * Returns the number of parent messages + 1.
1019 */
1020 PHP_METHOD(HttpMessage, count)
1021 {
1022 NO_ARGS {
1023 long i;
1024 http_message *msg;
1025 getObject(http_message_object, obj);
1026
1027 for (i = 0, msg = obj->message; msg; msg = msg->parent, ++i);
1028 RETURN_LONG(i);
1029 }
1030 }
1031 /* }}} */
1032
1033 /* {{{ proto string HttpMessage::serialize()
1034 *
1035 * Implements Serializable.
1036 *
1037 * Returns the serialized representation of the HttpMessage.
1038 */
1039 PHP_METHOD(HttpMessage, serialize)
1040 {
1041 NO_ARGS {
1042 char *string;
1043 size_t length;
1044 getObject(http_message_object, obj);
1045
1046 http_message_serialize(obj->message, &string, &length);
1047 RETURN_STRINGL(string, length, 0);
1048 }
1049 }
1050 /* }}} */
1051
1052 /* {{{ proto void HttpMessage::unserialize(string serialized)
1053 *
1054 * Implements Serializable.
1055 *
1056 * Re-constructs the HttpMessage based upon the serialized string.
1057 */
1058 PHP_METHOD(HttpMessage, unserialize)
1059 {
1060 int length;
1061 char *serialized;
1062 getObject(http_message_object, obj);
1063
1064 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &serialized, &length)) {
1065 http_message_dtor(obj->message);
1066 if (!http_message_parse_ex(obj->message, serialized, (size_t) length)) {
1067 http_error(HE_ERROR, HTTP_E_RUNTIME, "Could not unserialize HttpMessage");
1068 http_message_init(obj->message);
1069 }
1070 }
1071 }
1072 /* }}} */
1073
1074 #endif /* ZEND_ENGINE_2 */
1075
1076 /*
1077 * Local variables:
1078 * tab-width: 4
1079 * c-basic-offset: 4
1080 * End:
1081 * vim600: noet sw=4 ts=4 fdm=marker
1082 * vim<600: noet sw=4 ts=4
1083 */
1084