- add internal http_message_set_info()
[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 HTTP_EMPTY_ARGS(detach, 0);
98
99 HTTP_BEGIN_ARGS(prepend, 0, 1)
100 HTTP_ARG_OBJ(HttpMessage, message, 0)
101 HTTP_END_ARGS;
102
103 #define http_message_object_declare_default_properties() _http_message_object_declare_default_properties(TSRMLS_C)
104 static inline void _http_message_object_declare_default_properties(TSRMLS_D);
105 #define http_message_object_read_prop _http_message_object_read_prop
106 static zval *_http_message_object_read_prop(zval *object, zval *member, int type TSRMLS_DC);
107 #define http_message_object_write_prop _http_message_object_write_prop
108 static void _http_message_object_write_prop(zval *object, zval *member, zval *value TSRMLS_DC);
109 #define http_message_object_get_props _http_message_object_get_props
110 static HashTable *_http_message_object_get_props(zval *object TSRMLS_DC);
111
112 zend_class_entry *http_message_object_ce;
113 zend_function_entry http_message_object_fe[] = {
114 HTTP_MESSAGE_ME(__construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
115 HTTP_MESSAGE_ME(getBody, ZEND_ACC_PUBLIC)
116 HTTP_MESSAGE_ME(setBody, ZEND_ACC_PUBLIC)
117 HTTP_MESSAGE_ME(getHeaders, ZEND_ACC_PUBLIC)
118 HTTP_MESSAGE_ME(setHeaders, ZEND_ACC_PUBLIC)
119 HTTP_MESSAGE_ME(addHeaders, ZEND_ACC_PUBLIC)
120 HTTP_MESSAGE_ME(getType, ZEND_ACC_PUBLIC)
121 HTTP_MESSAGE_ME(setType, ZEND_ACC_PUBLIC)
122 HTTP_MESSAGE_ME(getResponseCode, ZEND_ACC_PUBLIC)
123 HTTP_MESSAGE_ME(setResponseCode, ZEND_ACC_PUBLIC)
124 HTTP_MESSAGE_ME(getRequestMethod, ZEND_ACC_PUBLIC)
125 HTTP_MESSAGE_ME(setRequestMethod, ZEND_ACC_PUBLIC)
126 HTTP_MESSAGE_ME(getRequestUrl, ZEND_ACC_PUBLIC)
127 HTTP_MESSAGE_ME(setRequestUrl, ZEND_ACC_PUBLIC)
128 HTTP_MESSAGE_ME(getHttpVersion, ZEND_ACC_PUBLIC)
129 HTTP_MESSAGE_ME(setHttpVersion, ZEND_ACC_PUBLIC)
130 HTTP_MESSAGE_ME(getParentMessage, ZEND_ACC_PUBLIC)
131 HTTP_MESSAGE_ME(send, ZEND_ACC_PUBLIC)
132 HTTP_MESSAGE_ME(toString, ZEND_ACC_PUBLIC)
133
134 /* implements Countable */
135 HTTP_MESSAGE_ME(count, ZEND_ACC_PUBLIC)
136
137 /* implements Serializable */
138 HTTP_MESSAGE_ME(serialize, ZEND_ACC_PUBLIC)
139 HTTP_MESSAGE_ME(unserialize, ZEND_ACC_PUBLIC)
140
141 ZEND_MALIAS(HttpMessage, __toString, toString, HTTP_ARGS(HttpMessage, toString), ZEND_ACC_PUBLIC)
142
143 HTTP_MESSAGE_ME(fromString, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
144
145 HTTP_MESSAGE_ME(detach, ZEND_ACC_PUBLIC)
146
147 HTTP_MESSAGE_ME(prepend, ZEND_ACC_PUBLIC)
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, requestUrl, "");
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 if (o->parent.handle) {
249 zval p;
250
251 INIT_PZVAL(&p);
252 p.type = IS_OBJECT;
253 p.value.obj = o->parent;
254 zend_objects_store_del_ref(&p TSRMLS_CC);
255 }
256 efree(o);
257 }
258
259 static zval *_http_message_object_read_prop(zval *object, zval *member, int type TSRMLS_DC)
260 {
261 getObjectEx(http_message_object, obj, object);
262 http_message *msg = obj->message;
263 zval *return_value;
264 #ifdef WONKY
265 ulong h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member)+1);
266 #else
267 zend_property_info *pinfo = zend_get_property_info(obj->zo.ce, member, 1 TSRMLS_CC);
268
269 if (!pinfo || ACC_PROP_PUBLIC(pinfo->flags)) {
270 return zend_get_std_object_handlers()->read_property(object, member, type TSRMLS_CC);
271 }
272 #endif
273
274 if (type == BP_VAR_W) {
275 zend_error(E_ERROR, "Cannot access HttpMessage properties by reference or array key/index");
276 return NULL;
277 }
278
279 ALLOC_ZVAL(return_value);
280 return_value->refcount = 0;
281 return_value->is_ref = 0;
282
283 #ifdef WONKY
284 switch (h)
285 #else
286 switch (pinfo->h)
287 #endif
288 {
289 case HTTP_MSG_PROPHASH_TYPE:
290 case HTTP_MSG_CHILD_PROPHASH_TYPE:
291 RETVAL_LONG(msg->type);
292 break;
293
294 case HTTP_MSG_PROPHASH_HTTP_VERSION:
295 case HTTP_MSG_CHILD_PROPHASH_HTTP_VERSION:
296 RETVAL_DOUBLE(msg->http.version);
297 break;
298
299 case HTTP_MSG_PROPHASH_BODY:
300 case HTTP_MSG_CHILD_PROPHASH_BODY:
301 phpstr_fix(PHPSTR(msg));
302 RETVAL_PHPSTR(PHPSTR(msg), 0, 1);
303 break;
304
305 case HTTP_MSG_PROPHASH_HEADERS:
306 case HTTP_MSG_CHILD_PROPHASH_HEADERS:
307 array_init(return_value);
308 zend_hash_copy(Z_ARRVAL_P(return_value), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
309 break;
310
311 case HTTP_MSG_PROPHASH_PARENT_MESSAGE:
312 case HTTP_MSG_CHILD_PROPHASH_PARENT_MESSAGE:
313 if (msg->parent) {
314 RETVAL_OBJVAL(obj->parent);
315 } else {
316 RETVAL_NULL();
317 }
318 break;
319
320 case HTTP_MSG_PROPHASH_REQUEST_METHOD:
321 case HTTP_MSG_CHILD_PROPHASH_REQUEST_METHOD:
322 if (HTTP_MSG_TYPE(REQUEST, msg) && msg->http.info.request.method) {
323 RETVAL_STRING(msg->http.info.request.method, 1);
324 } else {
325 RETVAL_NULL();
326 }
327 break;
328
329 case HTTP_MSG_PROPHASH_REQUEST_URL:
330 case HTTP_MSG_CHILD_PROPHASH_REQUEST_URL:
331 if (HTTP_MSG_TYPE(REQUEST, msg) && msg->http.info.request.url) {
332 RETVAL_STRING(msg->http.info.request.url, 1);
333 } else {
334 RETVAL_NULL();
335 }
336 break;
337
338 case HTTP_MSG_PROPHASH_RESPONSE_CODE:
339 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_CODE:
340 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
341 RETVAL_LONG(msg->http.info.response.code);
342 } else {
343 RETVAL_NULL();
344 }
345 break;
346
347 case HTTP_MSG_PROPHASH_RESPONSE_STATUS:
348 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_STATUS:
349 if (HTTP_MSG_TYPE(RESPONSE, msg) && msg->http.info.response.status) {
350 RETVAL_STRING(msg->http.info.response.status, 1);
351 } else {
352 RETVAL_NULL();
353 }
354 break;
355
356 default:
357 #ifdef WONKY
358 return zend_get_std_object_handlers()->read_property(object, member, type TSRMLS_CC);
359 #else
360 RETVAL_NULL();
361 #endif
362 break;
363 }
364
365 return return_value;
366 }
367
368 static void _http_message_object_write_prop(zval *object, zval *member, zval *value TSRMLS_DC)
369 {
370 getObjectEx(http_message_object, obj, object);
371 http_message *msg = obj->message;
372 zval *cpy = NULL;
373 #ifdef WONKY
374 ulong h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1);
375 #else
376 zend_property_info *pinfo = zend_get_property_info(obj->zo.ce, member, 1 TSRMLS_CC);
377
378 if (!pinfo || ACC_PROP_PUBLIC(pinfo->flags)) {
379 zend_get_std_object_handlers()->write_property(object, member, value TSRMLS_CC);
380 return;
381 }
382 #endif
383
384 cpy = zval_copy(Z_TYPE_P(value), value);
385
386 #ifdef WONKY
387 switch (h)
388 #else
389 switch (pinfo->h)
390 #endif
391 {
392 case HTTP_MSG_PROPHASH_TYPE:
393 case HTTP_MSG_CHILD_PROPHASH_TYPE:
394 convert_to_long(cpy);
395 http_message_set_type(msg, Z_LVAL_P(cpy));
396 break;
397
398 case HTTP_MSG_PROPHASH_HTTP_VERSION:
399 case HTTP_MSG_CHILD_PROPHASH_HTTP_VERSION:
400 convert_to_double(cpy);
401 msg->http.version = Z_DVAL_P(cpy);
402 break;
403
404 case HTTP_MSG_PROPHASH_BODY:
405 case HTTP_MSG_CHILD_PROPHASH_BODY:
406 convert_to_string(cpy);
407 phpstr_dtor(PHPSTR(msg));
408 phpstr_from_string_ex(PHPSTR(msg), Z_STRVAL_P(cpy), Z_STRLEN_P(cpy));
409 break;
410
411 case HTTP_MSG_PROPHASH_HEADERS:
412 case HTTP_MSG_CHILD_PROPHASH_HEADERS:
413 convert_to_array(cpy);
414 zend_hash_clean(&msg->hdrs);
415 zend_hash_copy(&msg->hdrs, Z_ARRVAL_P(cpy), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
416 break;
417
418 case HTTP_MSG_PROPHASH_PARENT_MESSAGE:
419 case HTTP_MSG_CHILD_PROPHASH_PARENT_MESSAGE:
420 if (Z_TYPE_P(value) == IS_OBJECT && instanceof_function(Z_OBJCE_P(value), http_message_object_ce TSRMLS_CC)) {
421 if (msg->parent) {
422 zval tmp;
423 tmp.value.obj = obj->parent;
424 Z_OBJ_DELREF(tmp);
425 }
426 Z_OBJ_ADDREF_P(value);
427 obj->parent = value->value.obj;
428 }
429 break;
430
431 case HTTP_MSG_PROPHASH_REQUEST_METHOD:
432 case HTTP_MSG_CHILD_PROPHASH_REQUEST_METHOD:
433 if (HTTP_MSG_TYPE(REQUEST, msg)) {
434 convert_to_string(cpy);
435 STR_SET(msg->http.info.request.method, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
436 }
437 break;
438
439 case HTTP_MSG_PROPHASH_REQUEST_URL:
440 case HTTP_MSG_CHILD_PROPHASH_REQUEST_URL:
441 if (HTTP_MSG_TYPE(REQUEST, msg)) {
442 convert_to_string(cpy);
443 STR_SET(msg->http.info.request.url, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
444 }
445 break;
446
447 case HTTP_MSG_PROPHASH_RESPONSE_CODE:
448 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_CODE:
449 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
450 convert_to_long(cpy);
451 msg->http.info.response.code = Z_LVAL_P(cpy);
452 }
453 break;
454
455 case HTTP_MSG_PROPHASH_RESPONSE_STATUS:
456 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_STATUS:
457 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
458 convert_to_string(cpy);
459 STR_SET(msg->http.info.response.status, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
460 }
461 break;
462
463 default:
464 #ifdef WONKY
465 zend_get_std_object_handlers()->write_property(object, member, value TSRMLS_CC);
466 #endif
467 break;
468 }
469 zval_free(&cpy);
470 }
471
472 static HashTable *_http_message_object_get_props(zval *object TSRMLS_DC)
473 {
474 zval *headers;
475 getObjectEx(http_message_object, obj, object);
476 http_message *msg = obj->message;
477 HashTable *props = OBJ_PROP(obj);
478 zval array;
479
480 INIT_ZARR(array, props);
481
482 #define ASSOC_PROP(array, ptype, name, val) \
483 { \
484 char *m_prop_name; \
485 int m_prop_len; \
486 zend_mangle_property_name(&m_prop_name, &m_prop_len, "*", 1, name, lenof(name), 0); \
487 add_assoc_ ##ptype## _ex(&array, m_prop_name, sizeof(name)+3, val); \
488 efree(m_prop_name); \
489 }
490 #define ASSOC_STRING(array, name, val) ASSOC_STRINGL(array, name, val, strlen(val))
491 #define ASSOC_STRINGL(array, name, val, len) \
492 { \
493 char *m_prop_name; \
494 int m_prop_len; \
495 zend_mangle_property_name(&m_prop_name, &m_prop_len, "*", 1, name, lenof(name), 0); \
496 add_assoc_stringl_ex(&array, m_prop_name, sizeof(name)+3, val, len, 1); \
497 efree(m_prop_name); \
498 }
499
500 ASSOC_PROP(array, long, "type", msg->type);
501 ASSOC_PROP(array, double, "httpVersion", msg->http.version);
502
503 switch (msg->type)
504 {
505 case HTTP_MSG_REQUEST:
506 ASSOC_PROP(array, long, "responseCode", 0);
507 ASSOC_STRINGL(array, "responseStatus", "", 0);
508 ASSOC_STRING(array, "requestMethod", msg->http.info.request.method);
509 ASSOC_STRING(array, "requestUrl", msg->http.info.request.url);
510 break;
511
512 case HTTP_MSG_RESPONSE:
513 ASSOC_PROP(array, long, "responseCode", msg->http.info.response.code);
514 ASSOC_STRING(array, "responseStatus", msg->http.info.response.status);
515 ASSOC_STRINGL(array, "requestMethod", "", 0);
516 ASSOC_STRINGL(array, "requestUrl", "", 0);
517 break;
518
519 case HTTP_MSG_NONE:
520 default:
521 ASSOC_PROP(array, long, "responseCode", 0);
522 ASSOC_STRINGL(array, "responseStatus", "", 0);
523 ASSOC_STRINGL(array, "requestMethod", "", 0);
524 ASSOC_STRINGL(array, "requestUrl", "", 0);
525 break;
526 }
527
528 MAKE_STD_ZVAL(headers);
529 array_init(headers);
530 zend_hash_copy(Z_ARRVAL_P(headers), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
531 ASSOC_PROP(array, zval, "headers", headers);
532 ASSOC_STRINGL(array, "body", PHPSTR_VAL(msg), PHPSTR_LEN(msg));
533
534 return OBJ_PROP(obj);
535 }
536
537 /* ### USERLAND ### */
538
539 /* {{{ proto void HttpMessage::__construct([string message])
540 *
541 * Instantiate a new HttpMessage object.
542 *
543 * Accepts an optional string parameter containing a single or several
544 * consecutive HTTP messages. The constructed object will actually
545 * represent the *last* message of the passed string. If there were
546 * prior messages, those can be accessed by HttpMessage::getParentMessage().
547 *
548 * Throws HttpMalformedHeaderException.
549 */
550 PHP_METHOD(HttpMessage, __construct)
551 {
552 int length = 0;
553 char *message = NULL;
554
555 getObject(http_message_object, obj);
556
557 SET_EH_THROW_HTTP();
558 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &message, &length) && message && length) {
559 http_message *msg = obj->message;
560
561 http_message_dtor(msg);
562 if ((obj->message = http_message_parse_ex(msg, message, length))) {
563 if (obj->message->parent) {
564 obj->parent = http_message_object_new_ex(Z_OBJCE_P(getThis()), obj->message->parent, NULL);
565 }
566 } else {
567 obj->message = http_message_init(msg);
568 }
569 }
570 if (!obj->message) {
571 obj->message = http_message_new();
572 }
573 SET_EH_NORMAL();
574 }
575 /* }}} */
576
577 /* {{{ proto static HttpMessage HttpMessage::fromString(string raw_message)
578 *
579 * Create an HttpMessage object from a string. Kind of a static constructor.
580 *
581 * Expects a string parameter containing a sinlge or several consecutive
582 * HTTP messages.
583 *
584 * Returns an HttpMessage object on success or NULL on failure.
585 *
586 * Throws HttpMalformedHeadersException.
587 */
588 PHP_METHOD(HttpMessage, fromString)
589 {
590 char *string = NULL;
591 int length = 0;
592 http_message *msg = NULL;
593
594 RETVAL_NULL();
595
596 SET_EH_THROW_HTTP();
597 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &string, &length)) {
598 if ((msg = http_message_parse(string, length))) {
599 ZVAL_OBJVAL(return_value, http_message_object_new_ex(http_message_object_ce, msg, NULL));
600 }
601 }
602 SET_EH_NORMAL();
603 }
604 /* }}} */
605
606 /* {{{ proto string HttpMessage::getBody()
607 *
608 * Get the body of the parsed HttpMessage.
609 *
610 * Returns the message body as string.
611 */
612 PHP_METHOD(HttpMessage, getBody)
613 {
614 NO_ARGS;
615
616 IF_RETVAL_USED {
617 getObject(http_message_object, obj);
618 RETURN_PHPSTR(&obj->message->body, PHPSTR_FREE_NOT, 1);
619 }
620 }
621 /* }}} */
622
623 /* {{{ proto void HttpMessage::setBody(string body)
624 *
625 * Set the body of the HttpMessage.
626 * NOTE: Don't forget to update any headers accordingly.
627 *
628 * Expects a string parameter containing the new body of the message.
629 */
630 PHP_METHOD(HttpMessage, setBody)
631 {
632 char *body;
633 int len;
634 getObject(http_message_object, obj);
635
636 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &body, &len)) {
637 phpstr_dtor(PHPSTR(obj->message));
638 phpstr_from_string_ex(PHPSTR(obj->message), body, len);
639 }
640 }
641 /* }}} */
642
643 /* {{{ proto array HttpMessage::getHeaders()
644 *
645 * Get Message Headers.
646 *
647 * Returns an associative array containing the messages HTTP headers.
648 */
649 PHP_METHOD(HttpMessage, getHeaders)
650 {
651 NO_ARGS;
652
653 IF_RETVAL_USED {
654 zval headers;
655 getObject(http_message_object, obj);
656
657 INIT_ZARR(headers, &obj->message->hdrs);
658 array_init(return_value);
659 array_copy(&headers, return_value);
660 }
661 }
662 /* }}} */
663
664 /* {{{ proto void HttpMessage::setHeaders(array headers)
665 *
666 * Sets new headers.
667 *
668 * Expects an associative array as parameter containing the new HTTP headers,
669 * which will replace *all* previous HTTP headers of the message.
670 */
671 PHP_METHOD(HttpMessage, setHeaders)
672 {
673 zval *new_headers, old_headers;
674 getObject(http_message_object, obj);
675
676 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &new_headers)) {
677 return;
678 }
679
680 zend_hash_clean(&obj->message->hdrs);
681 INIT_ZARR(old_headers, &obj->message->hdrs);
682 array_copy(new_headers, &old_headers);
683 }
684 /* }}} */
685
686 /* {{{ proto void HttpMessage::addHeaders(array headers[, bool append = false])
687 *
688 * Add headers. If append is true, headers with the same name will be separated, else overwritten.
689 *
690 * Expects an associative array as parameter containing the additional HTTP headers
691 * to add to the messages existing headers. If the optional bool parameter is true,
692 * and a header with the same name of one to add exists already, this respective
693 * header will be converted to an array containing both header values, otherwise
694 * it will be overwritten with the new header value.
695 */
696 PHP_METHOD(HttpMessage, addHeaders)
697 {
698 zval old_headers, *new_headers;
699 zend_bool append = 0;
700 getObject(http_message_object, obj);
701
702 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &new_headers, &append)) {
703 return;
704 }
705
706 INIT_ZARR(old_headers, &obj->message->hdrs);
707 if (append) {
708 array_append(new_headers, &old_headers);
709 } else {
710 array_merge(new_headers, &old_headers);
711 }
712 }
713 /* }}} */
714
715 /* {{{ proto int HttpMessage::getType()
716 *
717 * Get Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE)
718 *
719 * Returns the HttpMessage::TYPE.
720 */
721 PHP_METHOD(HttpMessage, getType)
722 {
723 NO_ARGS;
724
725 IF_RETVAL_USED {
726 getObject(http_message_object, obj);
727 RETURN_LONG(obj->message->type);
728 }
729 }
730 /* }}} */
731
732 /* {{{ proto void HttpMessage::setType(int type)
733 *
734 * Set Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE)
735 *
736 * Exptects an int parameter, the HttpMessage::TYPE.
737 */
738 PHP_METHOD(HttpMessage, setType)
739 {
740 long type;
741 getObject(http_message_object, obj);
742
743 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type)) {
744 return;
745 }
746 http_message_set_type(obj->message, type);
747 }
748 /* }}} */
749
750 /* {{{ proto int HttpMessage::getResponseCode()
751 *
752 * Get the Response Code of the Message.
753 *
754 * Returns the HTTP response code if the message is of type
755 * HttpMessage::TYPE_RESPONSE, else FALSE.
756 */
757 PHP_METHOD(HttpMessage, getResponseCode)
758 {
759 NO_ARGS;
760
761 IF_RETVAL_USED {
762 getObject(http_message_object, obj);
763 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
764 RETURN_LONG(obj->message->http.info.response.code);
765 }
766 }
767 /* }}} */
768
769 /* {{{ proto bool HttpMessage::setResponseCode(int code)
770 *
771 * Set the response code of an HTTP Response Message.
772 *
773 * Expects an int parameter with the HTTP response code.
774 *
775 * Returns TRUE on success, or FALSE if the message is not of type
776 * HttpMessage::TYPE_RESPONSE or the response code is out of range (100-510).
777 */
778 PHP_METHOD(HttpMessage, setResponseCode)
779 {
780 long code;
781 getObject(http_message_object, obj);
782
783 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
784
785 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) {
786 RETURN_FALSE;
787 }
788 if (code < 100 || code > 510) {
789 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid response code (100-510): %ld", code);
790 RETURN_FALSE;
791 }
792
793 obj->message->http.info.response.code = code;
794 RETURN_TRUE;
795 }
796 /* }}} */
797
798 /* {{{ proto string HttpMessage::getRequestMethod()
799 *
800 * Get the Request Method of the Message.
801 *
802 * Returns the request method name on success, or FALSE if the message is
803 * not of type HttpMessage::TYPE_REQUEST.
804 */
805 PHP_METHOD(HttpMessage, getRequestMethod)
806 {
807 NO_ARGS;
808
809 IF_RETVAL_USED {
810 getObject(http_message_object, obj);
811 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
812 RETURN_STRING(obj->message->http.info.request.method, 1);
813 }
814 }
815 /* }}} */
816
817 /* {{{ proto bool HttpMessage::setRequestMethod(string method)
818 *
819 * Set the Request Method of the HTTP Message.
820 *
821 * Expects a string parameter containing the request method name.
822 *
823 * Returns TRUE on success, or FALSE if the message is not of type
824 * HttpMessage::TYPE_REQUEST or an invalid request method was supplied.
825 */
826 PHP_METHOD(HttpMessage, setRequestMethod)
827 {
828 char *method;
829 int method_len;
830 getObject(http_message_object, obj);
831
832 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
833
834 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len)) {
835 RETURN_FALSE;
836 }
837 if (method_len < 1) {
838 http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestMethod to an empty string");
839 RETURN_FALSE;
840 }
841 if (SUCCESS != http_check_method(method)) {
842 http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Unkown request method: %s", method);
843 RETURN_FALSE;
844 }
845
846 STR_SET(obj->message->http.info.request.method, estrndup(method, method_len));
847 RETURN_TRUE;
848 }
849 /* }}} */
850
851 /* {{{ proto string HttpMessage::getRequestUrl()
852 *
853 * Get the Request URL of the Message.
854 *
855 * Returns the request url as string on success, or FALSE if the message
856 * is not of type HttpMessage::TYPE_REQUEST.
857 */
858 PHP_METHOD(HttpMessage, getRequestUrl)
859 {
860 NO_ARGS;
861
862 IF_RETVAL_USED {
863 getObject(http_message_object, obj);
864 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
865 RETURN_STRING(obj->message->http.info.request.url, 1);
866 }
867 }
868 /* }}} */
869
870 /* {{{ proto bool HttpMessage::setRequestUrl(string url)
871 *
872 * Set the Request URL of the HTTP Message.
873 *
874 * Expects a string parameters containing the request url.
875 *
876 * Returns TRUE on success, or FALSE if the message is not of type
877 * HttpMessage::TYPE_REQUEST or supplied URL was empty.
878 */
879 PHP_METHOD(HttpMessage, setRequestUrl)
880 {
881 char *URI;
882 int URIlen;
883 getObject(http_message_object, obj);
884
885 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &URI, &URIlen)) {
886 RETURN_FALSE;
887 }
888 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
889 if (URIlen < 1) {
890 http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestUrl to an empty string");
891 RETURN_FALSE;
892 }
893
894 STR_SET(obj->message->http.info.request.url, estrndup(URI, URIlen));
895 RETURN_TRUE;
896 }
897 /* }}} */
898
899 /* {{{ proto string HttpMessage::getHttpVersion()
900 *
901 * Get the HTTP Protocol Version of the Message.
902 *
903 * Returns the HTTP protocol version as string.
904 */
905 PHP_METHOD(HttpMessage, getHttpVersion)
906 {
907 NO_ARGS;
908
909 IF_RETVAL_USED {
910 char ver[4] = {0};
911 getObject(http_message_object, obj);
912
913 sprintf(ver, "%1.1lf", obj->message->http.version);
914 RETURN_STRINGL(ver, 3, 1);
915 }
916 }
917 /* }}} */
918
919 /* {{{ proto bool HttpMessage::setHttpVersion(string version)
920 *
921 * Set the HTTP Protocol version of the Message.
922 *
923 * Expects a string parameter containing the HTTP protocol version.
924 *
925 * Returns TRUE on success, or FALSE if supplied version is out of range (1.0/1.1).
926 */
927 PHP_METHOD(HttpMessage, setHttpVersion)
928 {
929 char v[4];
930 zval *zv;
931 getObject(http_message_object, obj);
932
933 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &zv)) {
934 return;
935 }
936
937 convert_to_double(zv);
938 sprintf(v, "%1.1lf", Z_DVAL_P(zv));
939 if (strcmp(v, "1.0") && strcmp(v, "1.1")) {
940 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid HTTP protocol version (1.0 or 1.1): %s", v);
941 RETURN_FALSE;
942 }
943
944 obj->message->http.version = Z_DVAL_P(zv);
945 RETURN_TRUE;
946 }
947 /* }}} */
948
949 /* {{{ proto HttpMessage HttpMessage::getParentMessage()
950 *
951 * Get parent Message.
952 *
953 * Returns the parent HttpMessage on success, or NULL if there's none.
954 */
955 PHP_METHOD(HttpMessage, getParentMessage)
956 {
957 NO_ARGS;
958
959 IF_RETVAL_USED {
960 getObject(http_message_object, obj);
961
962 if (obj->message->parent) {
963 RETVAL_OBJVAL(obj->parent);
964 } else {
965 RETVAL_NULL();
966 }
967 }
968 }
969 /* }}} */
970
971 /* {{{ proto bool HttpMessage::send()
972 *
973 * Send the Message according to its type as Response or Request.
974 * This provides limited functionality compared to HttpRequest and HttpResponse.
975 *
976 * Returns TRUE on success, or FALSE on failure.
977 */
978 PHP_METHOD(HttpMessage, send)
979 {
980 getObject(http_message_object, obj);
981
982 NO_ARGS;
983
984 RETURN_SUCCESS(http_message_send(obj->message));
985 }
986 /* }}} */
987
988 /* {{{ proto string HttpMessage::toString([bool include_parent = false])
989 *
990 * Get the string representation of the Message.
991 *
992 * Accepts a bool parameter which specifies whether the returned string
993 * should also contain any parent messages.
994 *
995 * Returns the full message as string.
996 */
997 PHP_METHOD(HttpMessage, toString)
998 {
999 IF_RETVAL_USED {
1000 char *string;
1001 size_t length;
1002 zend_bool include_parent = 0;
1003 getObject(http_message_object, obj);
1004
1005 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &include_parent)) {
1006 RETURN_FALSE;
1007 }
1008
1009 if (include_parent) {
1010 http_message_serialize(obj->message, &string, &length);
1011 } else {
1012 http_message_tostring(obj->message, &string, &length);
1013 }
1014 RETURN_STRINGL(string, length, 0);
1015 }
1016 }
1017 /* }}} */
1018
1019 /* {{{ proto int HttpMessage::count()
1020 *
1021 * Implements Countable.
1022 *
1023 * Returns the number of parent messages + 1.
1024 */
1025 PHP_METHOD(HttpMessage, count)
1026 {
1027 NO_ARGS {
1028 long i;
1029 http_message *msg;
1030 getObject(http_message_object, obj);
1031
1032 for (i = 0, msg = obj->message; msg; msg = msg->parent, ++i);
1033 RETURN_LONG(i);
1034 }
1035 }
1036 /* }}} */
1037
1038 /* {{{ proto string HttpMessage::serialize()
1039 *
1040 * Implements Serializable.
1041 *
1042 * Returns the serialized representation of the HttpMessage.
1043 */
1044 PHP_METHOD(HttpMessage, serialize)
1045 {
1046 NO_ARGS {
1047 char *string;
1048 size_t length;
1049 getObject(http_message_object, obj);
1050
1051 http_message_serialize(obj->message, &string, &length);
1052 RETURN_STRINGL(string, length, 0);
1053 }
1054 }
1055 /* }}} */
1056
1057 /* {{{ proto void HttpMessage::unserialize(string serialized)
1058 *
1059 * Implements Serializable.
1060 *
1061 * Re-constructs the HttpMessage based upon the serialized string.
1062 */
1063 PHP_METHOD(HttpMessage, unserialize)
1064 {
1065 int length;
1066 char *serialized;
1067 getObject(http_message_object, obj);
1068
1069 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &serialized, &length)) {
1070 http_message_dtor(obj->message);
1071 if (!http_message_parse_ex(obj->message, serialized, (size_t) length)) {
1072 http_error(HE_ERROR, HTTP_E_RUNTIME, "Could not unserialize HttpMessage");
1073 http_message_init(obj->message);
1074 }
1075 }
1076 }
1077 /* }}} */
1078
1079 /* {{{ proto HttpMessage HttpMessage::detach(void)
1080 *
1081 * Returns a clone of an HttpMessage object detached from any parent messages.
1082 */
1083 PHP_METHOD(HttpMessage, detach)
1084 {
1085 http_info info;
1086 http_message *msg;
1087 getObject(http_message_object, obj);
1088
1089 NO_ARGS;
1090
1091 info.type = obj->message->type;
1092 memcpy(&HTTP_INFO(&info), &HTTP_INFO(obj->message), sizeof(struct http_info));
1093
1094 msg = http_message_new();
1095 http_message_set_info(msg, &info);
1096
1097 zend_hash_copy(&msg->hdrs, &obj->message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
1098 phpstr_append(&msg->body, PHPSTR_VAL(obj->message), PHPSTR_LEN(obj->message));
1099
1100 RETURN_OBJVAL(http_message_object_new_ex(http_message_object_ce, msg, NULL));
1101 }
1102 /* }}} */
1103
1104 /* {{{ proto void HttpMessage::prepend(HttpMessage message)
1105 *
1106 * Prepends message(s) to the HTTP message.
1107 *
1108 * Expects an HttpMessage object as parameter.
1109 */
1110 PHP_METHOD(HttpMessage, prepend)
1111 {
1112 zval *prepend;
1113 zend_bool top = 1;
1114
1115 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|b", &prepend, http_message_object_ce, &top)) {
1116 zval m;
1117 http_message *save_parent_msg;
1118 zend_object_value save_parent_obj;
1119 getObject(http_message_object, obj);
1120 getObjectEx(http_message_object, prepend_obj, prepend);
1121
1122 INIT_PZVAL(&m);
1123 m.type = IS_OBJECT;
1124
1125 if (!top) {
1126 save_parent_obj = obj->parent;
1127 save_parent_msg = obj->message->parent;
1128 } else {
1129 /* iterate to the most parent object */
1130 while (obj->parent.handle) {
1131 m.value.obj = obj->parent;
1132 obj = zend_object_store_get_object(&m TSRMLS_CC);
1133 }
1134 }
1135
1136 /* prepend */
1137 obj->parent = prepend->value.obj;
1138 obj->message->parent = prepend_obj->message;
1139
1140 /* add ref */
1141 zend_objects_store_add_ref(prepend TSRMLS_CC);
1142 while (prepend_obj->parent.handle) {
1143 m.value.obj = prepend_obj->parent;
1144 zend_objects_store_add_ref(&m TSRMLS_CC);
1145 prepend_obj = zend_object_store_get_object(&m TSRMLS_CC);
1146 }
1147
1148 if (!top) {
1149 prepend_obj->parent = save_parent_obj;
1150 prepend_obj->message->parent = save_parent_msg;
1151 }
1152 }
1153 }
1154 /* }}} */
1155
1156 #endif /* ZEND_ENGINE_2 */
1157
1158 /*
1159 * Local variables:
1160 * tab-width: 4
1161 * c-basic-offset: 4
1162 * End:
1163 * vim600: noet sw=4 ts=4 fdm=marker
1164 * vim<600: noet sw=4 ts=4
1165 */
1166