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