- use zend_hash_find() instead of FOREACH in http_match_request_header() (don't ask)
[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 #ifdef WONKY
365 ulong h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1);
366 #else
367 zend_property_info *pinfo = zend_get_property_info(obj->zo.ce, member, 1 TSRMLS_CC);
368
369 if (!pinfo || ACC_PROP_PUBLIC(pinfo->flags)) {
370 zend_get_std_object_handlers()->write_property(object, member, value TSRMLS_CC);
371 return;
372 }
373 #endif
374
375 #ifdef WONKY
376 switch (h)
377 #else
378 switch (pinfo->h)
379 #endif
380 {
381 case HTTP_MSG_PROPHASH_TYPE:
382 case HTTP_MSG_CHILD_PROPHASH_TYPE:
383 convert_to_long_ex(&value);
384 http_message_set_type(msg, Z_LVAL_P(value));
385 break;
386
387 case HTTP_MSG_PROPHASH_HTTP_VERSION:
388 case HTTP_MSG_CHILD_PROPHASH_HTTP_VERSION:
389 convert_to_double_ex(&value);
390 msg->http.version = Z_DVAL_P(value);
391 break;
392
393 case HTTP_MSG_PROPHASH_BODY:
394 case HTTP_MSG_CHILD_PROPHASH_BODY:
395 convert_to_string_ex(&value);
396 phpstr_dtor(PHPSTR(msg));
397 phpstr_from_string_ex(PHPSTR(msg), Z_STRVAL_P(value), Z_STRLEN_P(value));
398 break;
399
400 case HTTP_MSG_PROPHASH_HEADERS:
401 case HTTP_MSG_CHILD_PROPHASH_HEADERS:
402 convert_to_array_ex(&value);
403 zend_hash_clean(&msg->hdrs);
404 zend_hash_copy(&msg->hdrs, Z_ARRVAL_P(value), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
405 break;
406
407 case HTTP_MSG_PROPHASH_PARENT_MESSAGE:
408 case HTTP_MSG_CHILD_PROPHASH_PARENT_MESSAGE:
409 if (Z_TYPE_P(value) == IS_OBJECT && instanceof_function(Z_OBJCE_P(value), http_message_object_ce TSRMLS_CC)) {
410 if (msg->parent) {
411 zval tmp;
412 tmp.value.obj = obj->parent;
413 Z_OBJ_DELREF(tmp);
414 }
415 Z_OBJ_ADDREF_P(value);
416 obj->parent = value->value.obj;
417 }
418 break;
419
420 case HTTP_MSG_PROPHASH_REQUEST_METHOD:
421 case HTTP_MSG_CHILD_PROPHASH_REQUEST_METHOD:
422 if (HTTP_MSG_TYPE(REQUEST, msg)) {
423 convert_to_string_ex(&value);
424 STR_SET(msg->http.info.request.method, estrndup(Z_STRVAL_P(value), Z_STRLEN_P(value)));
425 }
426 break;
427
428 case HTTP_MSG_PROPHASH_REQUEST_URI:
429 case HTTP_MSG_CHILD_PROPHASH_REQUEST_URI:
430 if (HTTP_MSG_TYPE(REQUEST, msg)) {
431 convert_to_string_ex(&value);
432 STR_SET(msg->http.info.request.URI, estrndup(Z_STRVAL_P(value), Z_STRLEN_P(value)));
433 }
434 break;
435
436 case HTTP_MSG_PROPHASH_RESPONSE_CODE:
437 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_CODE:
438 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
439 convert_to_long_ex(&value);
440 msg->http.info.response.code = Z_LVAL_P(value);
441 }
442 break;
443
444 case HTTP_MSG_PROPHASH_RESPONSE_STATUS:
445 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_STATUS:
446 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
447 convert_to_string_ex(&value);
448 STR_SET(msg->http.info.response.status, estrndup(Z_STRVAL_P(value), Z_STRLEN_P(value)));
449 }
450 break;
451
452 default:
453 #ifdef WONKY
454 zend_get_std_object_handlers()->write_property(object, member, value TSRMLS_CC);
455 #endif
456 break;
457 }
458 }
459
460 static HashTable *_http_message_object_get_props(zval *object TSRMLS_DC)
461 {
462 zval *headers;
463 getObjectEx(http_message_object, obj, object);
464 http_message *msg = obj->message;
465 HashTable *props = OBJ_PROP(obj);
466 zval array;
467
468 INIT_ZARR(array, props);
469
470 #define ASSOC_PROP(array, ptype, name, val) \
471 { \
472 char *m_prop_name; \
473 int m_prop_len; \
474 zend_mangle_property_name(&m_prop_name, &m_prop_len, "*", 1, name, lenof(name), 0); \
475 add_assoc_ ##ptype## _ex(&array, m_prop_name, sizeof(name)+4, val); \
476 efree(m_prop_name); \
477 }
478 #define ASSOC_STRING(array, name, val) ASSOC_STRINGL(array, name, val, strlen(val))
479 #define ASSOC_STRINGL(array, name, val, len) \
480 { \
481 char *m_prop_name; \
482 int m_prop_len; \
483 zend_mangle_property_name(&m_prop_name, &m_prop_len, "*", 1, name, lenof(name), 0); \
484 add_assoc_stringl_ex(&array, m_prop_name, sizeof(name)+4, val, len, 1); \
485 efree(m_prop_name); \
486 }
487
488 ASSOC_PROP(array, long, "type", msg->type);
489 ASSOC_PROP(array, double, "httpVersion", msg->http.version);
490
491 switch (msg->type)
492 {
493 case HTTP_MSG_REQUEST:
494 ASSOC_PROP(array, long, "responseCode", 0);
495 ASSOC_STRINGL(array, "responseStatus", "", 0);
496 ASSOC_STRING(array, "requestMethod", msg->http.info.request.method);
497 ASSOC_STRING(array, "requestUri", msg->http.info.request.URI);
498 break;
499
500 case HTTP_MSG_RESPONSE:
501 ASSOC_PROP(array, long, "responseCode", msg->http.info.response.code);
502 ASSOC_STRING(array, "responseStatus", msg->http.info.response.status);
503 ASSOC_STRINGL(array, "requestMethod", "", 0);
504 ASSOC_STRINGL(array, "requestUri", "", 0);
505 break;
506
507 case HTTP_MSG_NONE:
508 default:
509 ASSOC_PROP(array, long, "responseCode", 0);
510 ASSOC_STRINGL(array, "responseStatus", "", 0);
511 ASSOC_STRINGL(array, "requestMethod", "", 0);
512 ASSOC_STRINGL(array, "requestUri", "", 0);
513 break;
514 }
515
516 MAKE_STD_ZVAL(headers);
517 array_init(headers);
518 zend_hash_copy(Z_ARRVAL_P(headers), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
519 ASSOC_PROP(array, zval, "headers", headers);
520 ASSOC_STRINGL(array, "body", PHPSTR_VAL(msg), PHPSTR_LEN(msg));
521
522 return OBJ_PROP(obj);
523 }
524
525 /* ### USERLAND ### */
526
527 /* {{{ proto void HttpMessage::__construct([string message])
528 *
529 * Instantiate a new HttpMessage object.
530 *
531 * Accepts an optional string parameter containing a single or several
532 * consecutive HTTP messages. The constructed object will actually
533 * represent the *last* message of the passed string. If there were
534 * prior messages, those can be accessed by HttpMessage::getParentMessage().
535 *
536 * Throws HttpMalformedHeaderException.
537 */
538 PHP_METHOD(HttpMessage, __construct)
539 {
540 int length = 0;
541 char *message = NULL;
542
543 getObject(http_message_object, obj);
544
545 SET_EH_THROW_HTTP();
546 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &message, &length) && message && length) {
547 http_message *msg = obj->message;
548
549 http_message_dtor(msg);
550 if (obj->message = http_message_parse_ex(msg, message, length)) {
551 if (obj->message->parent) {
552 obj->parent = http_message_object_new_ex(Z_OBJCE_P(getThis()), obj->message->parent, NULL);
553 }
554 } else {
555 obj->message = http_message_init(msg);
556 }
557 }
558 if (!obj->message) {
559 obj->message = http_message_new();
560 }
561 SET_EH_NORMAL();
562 }
563 /* }}} */
564
565 /* {{{ proto static HttpMessage HttpMessage::fromString(string raw_message)
566 *
567 * Create an HttpMessage object from a string. Kind of a static constructor.
568 *
569 * Expects a string parameter containing a sinlge or several consecutive
570 * HTTP messages.
571 *
572 * Returns an HttpMessage object on success or NULL on failure.
573 *
574 * Throws HttpMalformedHeadersException.
575 */
576 PHP_METHOD(HttpMessage, fromString)
577 {
578 char *string = NULL;
579 int length = 0;
580 http_message *msg = NULL;
581
582 RETVAL_NULL();
583
584 SET_EH_THROW_HTTP();
585 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &string, &length)) {
586 if (msg = http_message_parse(string, length)) {
587 ZVAL_OBJVAL(return_value, http_message_object_new_ex(http_message_object_ce, msg, NULL));
588 }
589 }
590 SET_EH_NORMAL();
591 }
592 /* }}} */
593
594 /* {{{ proto string HttpMessage::getBody()
595 *
596 * Get the body of the parsed HttpMessage.
597 *
598 * Returns the message body as string.
599 */
600 PHP_METHOD(HttpMessage, getBody)
601 {
602 NO_ARGS;
603
604 IF_RETVAL_USED {
605 getObject(http_message_object, obj);
606 RETURN_PHPSTR(&obj->message->body, PHPSTR_FREE_NOT, 1);
607 }
608 }
609 /* }}} */
610
611 /* {{{ proto void HttpMessage::setBody(string body)
612 *
613 * Set the body of the HttpMessage.
614 * NOTE: Don't forget to update any headers accordingly.
615 *
616 * Expects a string parameter containing the new body of the message.
617 */
618 PHP_METHOD(HttpMessage, setBody)
619 {
620 char *body;
621 int len;
622 getObject(http_message_object, obj);
623
624 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &body, &len)) {
625 phpstr_dtor(PHPSTR(obj->message));
626 phpstr_from_string_ex(PHPSTR(obj->message), body, len);
627 }
628 }
629 /* }}} */
630
631 /* {{{ proto array HttpMessage::getHeaders()
632 *
633 * Get Message Headers.
634 *
635 * Returns an associative array containing the messages HTTP headers.
636 */
637 PHP_METHOD(HttpMessage, getHeaders)
638 {
639 NO_ARGS;
640
641 IF_RETVAL_USED {
642 zval headers;
643 getObject(http_message_object, obj);
644
645 INIT_ZARR(headers, &obj->message->hdrs);
646 array_init(return_value);
647 array_copy(&headers, return_value);
648 }
649 }
650 /* }}} */
651
652 /* {{{ proto void HttpMessage::setHeaders(array headers)
653 *
654 * Sets new headers.
655 *
656 * Expects an associative array as parameter containing the new HTTP headers,
657 * which will replace *all* previous HTTP headers of the message.
658 */
659 PHP_METHOD(HttpMessage, setHeaders)
660 {
661 zval *new_headers, old_headers;
662 getObject(http_message_object, obj);
663
664 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &new_headers)) {
665 return;
666 }
667
668 zend_hash_clean(&obj->message->hdrs);
669 INIT_ZARR(old_headers, &obj->message->hdrs);
670 array_copy(new_headers, &old_headers);
671 }
672 /* }}} */
673
674 /* {{{ proto void HttpMessage::addHeaders(array headers[, bool append = false])
675 *
676 * Add headers. If append is true, headers with the same name will be separated, else overwritten.
677 *
678 * Expects an associative array as parameter containing the additional HTTP headers
679 * to add to the messages existing headers. If the optional bool parameter is true,
680 * and a header with the same name of one to add exists already, this respective
681 * header will be converted to an array containing both header values, otherwise
682 * it will be overwritten with the new header value.
683 */
684 PHP_METHOD(HttpMessage, addHeaders)
685 {
686 zval old_headers, *new_headers;
687 zend_bool append = 0;
688 getObject(http_message_object, obj);
689
690 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &new_headers, &append)) {
691 return;
692 }
693
694 INIT_ZARR(old_headers, &obj->message->hdrs);
695 if (append) {
696 array_append(new_headers, &old_headers);
697 } else {
698 array_merge(new_headers, &old_headers);
699 }
700 }
701 /* }}} */
702
703 /* {{{ proto int HttpMessage::getType()
704 *
705 * Get Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE)
706 *
707 * Returns the HttpMessage::TYPE.
708 */
709 PHP_METHOD(HttpMessage, getType)
710 {
711 NO_ARGS;
712
713 IF_RETVAL_USED {
714 getObject(http_message_object, obj);
715 RETURN_LONG(obj->message->type);
716 }
717 }
718 /* }}} */
719
720 /* {{{ proto void HttpMessage::setType(int type)
721 *
722 * Set Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE)
723 *
724 * Exptects an int parameter, the HttpMessage::TYPE.
725 */
726 PHP_METHOD(HttpMessage, setType)
727 {
728 long type;
729 getObject(http_message_object, obj);
730
731 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type)) {
732 return;
733 }
734 http_message_set_type(obj->message, type);
735 }
736 /* }}} */
737
738 /* {{{ proto int HttpMessage::getResponseCode()
739 *
740 * Get the Response Code of the Message.
741 *
742 * Returns the HTTP response code if the message is of type
743 * HttpMessage::TYPE_RESPONSE, else FALSE.
744 */
745 PHP_METHOD(HttpMessage, getResponseCode)
746 {
747 NO_ARGS;
748
749 IF_RETVAL_USED {
750 getObject(http_message_object, obj);
751 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
752 RETURN_LONG(obj->message->http.info.response.code);
753 }
754 }
755 /* }}} */
756
757 /* {{{ proto bool HttpMessage::setResponseCode(int code)
758 *
759 * Set the response code of an HTTP Response Message.
760 *
761 * Expects an int parameter with the HTTP response code.
762 *
763 * Returns TRUE on success, or FALSE if the message is not of type
764 * HttpMessage::TYPE_RESPONSE or the response code is out of range (100-510).
765 */
766 PHP_METHOD(HttpMessage, setResponseCode)
767 {
768 long code;
769 getObject(http_message_object, obj);
770
771 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
772
773 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) {
774 RETURN_FALSE;
775 }
776 if (code < 100 || code > 510) {
777 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid response code (100-510): %ld", code);
778 RETURN_FALSE;
779 }
780
781 obj->message->http.info.response.code = code;
782 RETURN_TRUE;
783 }
784 /* }}} */
785
786 /* {{{ proto string HttpMessage::getRequestMethod()
787 *
788 * Get the Request Method of the Message.
789 *
790 * Returns the request method name on success, or FALSE if the message is
791 * not of type HttpMessage::TYPE_REQUEST.
792 */
793 PHP_METHOD(HttpMessage, getRequestMethod)
794 {
795 NO_ARGS;
796
797 IF_RETVAL_USED {
798 getObject(http_message_object, obj);
799 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
800 RETURN_STRING(obj->message->http.info.request.method, 1);
801 }
802 }
803 /* }}} */
804
805 /* {{{ proto bool HttpMessage::setRequestMethod(string method)
806 *
807 * Set the Request Method of the HTTP Message.
808 *
809 * Expects a string parameter containing the request method name.
810 *
811 * Returns TRUE on success, or FALSE if the message is not of type
812 * HttpMessage::TYPE_REQUEST or an invalid request method was supplied.
813 */
814 PHP_METHOD(HttpMessage, setRequestMethod)
815 {
816 char *method;
817 int method_len;
818 getObject(http_message_object, obj);
819
820 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
821
822 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len)) {
823 RETURN_FALSE;
824 }
825 if (method_len < 1) {
826 http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestMethod to an empty string");
827 RETURN_FALSE;
828 }
829 if (SUCCESS != http_check_method(method)) {
830 http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Unkown request method: %s", method);
831 RETURN_FALSE;
832 }
833
834 STR_SET(obj->message->http.info.request.method, estrndup(method, method_len));
835 RETURN_TRUE;
836 }
837 /* }}} */
838
839 /* {{{ proto string HttpMessage::getRequestUri()
840 *
841 * Get the Request URI of the Message.
842 *
843 * Returns the request uri as string on success, or FALSE if the message
844 * is not of type HttpMessage::TYPE_REQUEST.
845 */
846 PHP_METHOD(HttpMessage, getRequestUri)
847 {
848 NO_ARGS;
849
850 IF_RETVAL_USED {
851 getObject(http_message_object, obj);
852 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
853 RETURN_STRING(obj->message->http.info.request.URI, 1);
854 }
855 }
856 /* }}} */
857
858 /* {{{ proto bool HttpMessage::setRequestUri(string URI)
859 *
860 * Set the Request URI of the HTTP Message.
861 *
862 * Expects a string parameters containing the request uri.
863 *
864 * Returns TRUE on success, or FALSE if the message is not of type
865 * HttpMessage::TYPE_REQUEST or supplied URI was empty.
866 */
867 PHP_METHOD(HttpMessage, setRequestUri)
868 {
869 char *URI;
870 int URIlen;
871 getObject(http_message_object, obj);
872
873 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &URI, &URIlen)) {
874 RETURN_FALSE;
875 }
876 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
877 if (URIlen < 1) {
878 http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestUri to an empty string");
879 RETURN_FALSE;
880 }
881
882 STR_SET(obj->message->http.info.request.URI, estrndup(URI, URIlen));
883 RETURN_TRUE;
884 }
885 /* }}} */
886
887 /* {{{ proto string HttpMessage::getHttpVersion()
888 *
889 * Get the HTTP Protocol Version of the Message.
890 *
891 * Returns the HTTP protocol version as string.
892 */
893 PHP_METHOD(HttpMessage, getHttpVersion)
894 {
895 NO_ARGS;
896
897 IF_RETVAL_USED {
898 char ver[4] = {0};
899 getObject(http_message_object, obj);
900
901 sprintf(ver, "%1.1lf", obj->message->http.version);
902 RETURN_STRINGL(ver, 3, 1);
903 }
904 }
905 /* }}} */
906
907 /* {{{ proto bool HttpMessage::setHttpVersion(string version)
908 *
909 * Set the HTTP Protocol version of the Message.
910 *
911 * Expects a string parameter containing the HTTP protocol version.
912 *
913 * Returns TRUE on success, or FALSE if supplied version is out of range (1.0/1.1).
914 */
915 PHP_METHOD(HttpMessage, setHttpVersion)
916 {
917 char v[4];
918 zval *zv;
919 getObject(http_message_object, obj);
920
921 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &zv)) {
922 return;
923 }
924
925 convert_to_double(zv);
926 sprintf(v, "%1.1lf", Z_DVAL_P(zv));
927 if (strcmp(v, "1.0") && strcmp(v, "1.1")) {
928 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid HTTP protocol version (1.0 or 1.1): %s", v);
929 RETURN_FALSE;
930 }
931
932 obj->message->http.version = Z_DVAL_P(zv);
933 RETURN_TRUE;
934 }
935 /* }}} */
936
937 /* {{{ proto HttpMessage HttpMessage::getParentMessage()
938 *
939 * Get parent Message.
940 *
941 * Returns the parent HttpMessage on success, or NULL if there's none.
942 */
943 PHP_METHOD(HttpMessage, getParentMessage)
944 {
945 NO_ARGS;
946
947 IF_RETVAL_USED {
948 getObject(http_message_object, obj);
949
950 if (obj->message->parent) {
951 RETVAL_OBJVAL(obj->parent);
952 } else {
953 RETVAL_NULL();
954 }
955 }
956 }
957 /* }}} */
958
959 /* {{{ proto bool HttpMessage::send()
960 *
961 * Send the Message according to its type as Response or Request.
962 * This provides limited functionality compared to HttpRequest and HttpResponse.
963 *
964 * Returns TRUE on success, or FALSE on failure.
965 */
966 PHP_METHOD(HttpMessage, send)
967 {
968 getObject(http_message_object, obj);
969
970 NO_ARGS;
971
972 RETURN_SUCCESS(http_message_send(obj->message));
973 }
974 /* }}} */
975
976 /* {{{ proto string HttpMessage::toString([bool include_parent = false])
977 *
978 * Get the string representation of the Message.
979 *
980 * Accepts a bool parameter which specifies whether the returned string
981 * should also contain any parent messages.
982 *
983 * Returns the full message as string.
984 */
985 PHP_METHOD(HttpMessage, toString)
986 {
987 IF_RETVAL_USED {
988 char *string;
989 size_t length;
990 zend_bool include_parent = 0;
991 getObject(http_message_object, obj);
992
993 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &include_parent)) {
994 RETURN_FALSE;
995 }
996
997 if (include_parent) {
998 http_message_serialize(obj->message, &string, &length);
999 } else {
1000 http_message_tostring(obj->message, &string, &length);
1001 }
1002 RETURN_STRINGL(string, length, 0);
1003 }
1004 }
1005 /* }}} */
1006
1007 /* {{{ proto int HttpMessage::count()
1008 *
1009 * Implements Countable.
1010 *
1011 * Returns the number of parent messages + 1.
1012 */
1013 PHP_METHOD(HttpMessage, count)
1014 {
1015 NO_ARGS {
1016 long i;
1017 http_message *msg;
1018 getObject(http_message_object, obj);
1019
1020 for (i = 0, msg = obj->message; msg; msg = msg->parent, ++i);
1021 RETURN_LONG(i);
1022 }
1023 }
1024 /* }}} */
1025
1026 /* {{{ proto string HttpMessage::serialize()
1027 *
1028 * Implements Serializable.
1029 *
1030 * Returns the serialized representation of the HttpMessage.
1031 */
1032 PHP_METHOD(HttpMessage, serialize)
1033 {
1034 NO_ARGS {
1035 char *string;
1036 size_t length;
1037 getObject(http_message_object, obj);
1038
1039 http_message_serialize(obj->message, &string, &length);
1040 RETURN_STRINGL(string, length, 0);
1041 }
1042 }
1043 /* }}} */
1044
1045 /* {{{ proto void HttpMessage::unserialize(string serialized)
1046 *
1047 * Implements Serializable.
1048 *
1049 * Re-constructs the HttpMessage based upon the serialized string.
1050 */
1051 PHP_METHOD(HttpMessage, unserialize)
1052 {
1053 int length;
1054 char *serialized;
1055 getObject(http_message_object, obj);
1056
1057 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &serialized, &length)) {
1058 http_message_dtor(obj->message);
1059 if (!http_message_parse_ex(obj->message, serialized, (size_t) length)) {
1060 http_error(HE_ERROR, HTTP_E_RUNTIME, "Could not unserialize HttpMessage");
1061 http_message_init(obj->message);
1062 }
1063 }
1064 }
1065 /* }}} */
1066
1067 #endif /* ZEND_ENGINE_2 */
1068
1069 /*
1070 * Local variables:
1071 * tab-width: 4
1072 * c-basic-offset: 4
1073 * End:
1074 * vim600: noet sw=4 ts=4 fdm=marker
1075 * vim<600: noet sw=4 ts=4
1076 */
1077