- honor class of the current object
[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[, string class_name = "HttpMessage"])
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. Accepts an optionsl string parameter specifying the class to use.
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, *class_name = NULL;
591 int length = 0, class_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|s", &string, &length, &class_name, &class_length)) {
598 if ((msg = http_message_parse(string, length))) {
599 zend_class_entry *ce = http_message_object_ce;
600
601 if (class_name && *class_name) {
602 ce = zend_fetch_class(class_name, class_length, ZEND_FETCH_CLASS_DEFAULT TSRMLS_CC);
603 }
604 if (ce) {
605 ZVAL_OBJVAL(return_value, http_message_object_new_ex(ce, msg, NULL));
606 }
607 }
608 }
609 SET_EH_NORMAL();
610 }
611 /* }}} */
612
613 /* {{{ proto string HttpMessage::getBody()
614 *
615 * Get the body of the parsed HttpMessage.
616 *
617 * Returns the message body as string.
618 */
619 PHP_METHOD(HttpMessage, getBody)
620 {
621 NO_ARGS;
622
623 IF_RETVAL_USED {
624 getObject(http_message_object, obj);
625 RETURN_PHPSTR(&obj->message->body, PHPSTR_FREE_NOT, 1);
626 }
627 }
628 /* }}} */
629
630 /* {{{ proto void HttpMessage::setBody(string body)
631 *
632 * Set the body of the HttpMessage.
633 * NOTE: Don't forget to update any headers accordingly.
634 *
635 * Expects a string parameter containing the new body of the message.
636 */
637 PHP_METHOD(HttpMessage, setBody)
638 {
639 char *body;
640 int len;
641 getObject(http_message_object, obj);
642
643 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &body, &len)) {
644 phpstr_dtor(PHPSTR(obj->message));
645 phpstr_from_string_ex(PHPSTR(obj->message), body, len);
646 }
647 }
648 /* }}} */
649
650 /* {{{ proto array HttpMessage::getHeaders()
651 *
652 * Get Message Headers.
653 *
654 * Returns an associative array containing the messages HTTP headers.
655 */
656 PHP_METHOD(HttpMessage, getHeaders)
657 {
658 NO_ARGS;
659
660 IF_RETVAL_USED {
661 zval headers;
662 getObject(http_message_object, obj);
663
664 INIT_ZARR(headers, &obj->message->hdrs);
665 array_init(return_value);
666 array_copy(&headers, return_value);
667 }
668 }
669 /* }}} */
670
671 /* {{{ proto void HttpMessage::setHeaders(array headers)
672 *
673 * Sets new headers.
674 *
675 * Expects an associative array as parameter containing the new HTTP headers,
676 * which will replace *all* previous HTTP headers of the message.
677 */
678 PHP_METHOD(HttpMessage, setHeaders)
679 {
680 zval *new_headers, old_headers;
681 getObject(http_message_object, obj);
682
683 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &new_headers)) {
684 return;
685 }
686
687 zend_hash_clean(&obj->message->hdrs);
688 INIT_ZARR(old_headers, &obj->message->hdrs);
689 array_copy(new_headers, &old_headers);
690 }
691 /* }}} */
692
693 /* {{{ proto void HttpMessage::addHeaders(array headers[, bool append = false])
694 *
695 * Add headers. If append is true, headers with the same name will be separated, else overwritten.
696 *
697 * Expects an associative array as parameter containing the additional HTTP headers
698 * to add to the messages existing headers. If the optional bool parameter is true,
699 * and a header with the same name of one to add exists already, this respective
700 * header will be converted to an array containing both header values, otherwise
701 * it will be overwritten with the new header value.
702 */
703 PHP_METHOD(HttpMessage, addHeaders)
704 {
705 zval old_headers, *new_headers;
706 zend_bool append = 0;
707 getObject(http_message_object, obj);
708
709 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &new_headers, &append)) {
710 return;
711 }
712
713 INIT_ZARR(old_headers, &obj->message->hdrs);
714 if (append) {
715 array_append(new_headers, &old_headers);
716 } else {
717 array_merge(new_headers, &old_headers);
718 }
719 }
720 /* }}} */
721
722 /* {{{ proto int HttpMessage::getType()
723 *
724 * Get Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE)
725 *
726 * Returns the HttpMessage::TYPE.
727 */
728 PHP_METHOD(HttpMessage, getType)
729 {
730 NO_ARGS;
731
732 IF_RETVAL_USED {
733 getObject(http_message_object, obj);
734 RETURN_LONG(obj->message->type);
735 }
736 }
737 /* }}} */
738
739 /* {{{ proto void HttpMessage::setType(int type)
740 *
741 * Set Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE)
742 *
743 * Exptects an int parameter, the HttpMessage::TYPE.
744 */
745 PHP_METHOD(HttpMessage, setType)
746 {
747 long type;
748 getObject(http_message_object, obj);
749
750 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type)) {
751 return;
752 }
753 http_message_set_type(obj->message, type);
754 }
755 /* }}} */
756
757 /* {{{ proto int HttpMessage::getResponseCode()
758 *
759 * Get the Response Code of the Message.
760 *
761 * Returns the HTTP response code if the message is of type
762 * HttpMessage::TYPE_RESPONSE, else FALSE.
763 */
764 PHP_METHOD(HttpMessage, getResponseCode)
765 {
766 NO_ARGS;
767
768 IF_RETVAL_USED {
769 getObject(http_message_object, obj);
770 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
771 RETURN_LONG(obj->message->http.info.response.code);
772 }
773 }
774 /* }}} */
775
776 /* {{{ proto bool HttpMessage::setResponseCode(int code)
777 *
778 * Set the response code of an HTTP Response Message.
779 *
780 * Expects an int parameter with the HTTP response code.
781 *
782 * Returns TRUE on success, or FALSE if the message is not of type
783 * HttpMessage::TYPE_RESPONSE or the response code is out of range (100-510).
784 */
785 PHP_METHOD(HttpMessage, setResponseCode)
786 {
787 long code;
788 getObject(http_message_object, obj);
789
790 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
791
792 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) {
793 RETURN_FALSE;
794 }
795 if (code < 100 || code > 510) {
796 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid response code (100-510): %ld", code);
797 RETURN_FALSE;
798 }
799
800 obj->message->http.info.response.code = code;
801 RETURN_TRUE;
802 }
803 /* }}} */
804
805 /* {{{ proto string HttpMessage::getRequestMethod()
806 *
807 * Get the Request Method of the Message.
808 *
809 * Returns the request method name on success, or FALSE if the message is
810 * not of type HttpMessage::TYPE_REQUEST.
811 */
812 PHP_METHOD(HttpMessage, getRequestMethod)
813 {
814 NO_ARGS;
815
816 IF_RETVAL_USED {
817 getObject(http_message_object, obj);
818 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
819 RETURN_STRING(obj->message->http.info.request.method, 1);
820 }
821 }
822 /* }}} */
823
824 /* {{{ proto bool HttpMessage::setRequestMethod(string method)
825 *
826 * Set the Request Method of the HTTP Message.
827 *
828 * Expects a string parameter containing the request method name.
829 *
830 * Returns TRUE on success, or FALSE if the message is not of type
831 * HttpMessage::TYPE_REQUEST or an invalid request method was supplied.
832 */
833 PHP_METHOD(HttpMessage, setRequestMethod)
834 {
835 char *method;
836 int method_len;
837 getObject(http_message_object, obj);
838
839 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
840
841 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len)) {
842 RETURN_FALSE;
843 }
844 if (method_len < 1) {
845 http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestMethod to an empty string");
846 RETURN_FALSE;
847 }
848 if (SUCCESS != http_check_method(method)) {
849 http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Unkown request method: %s", method);
850 RETURN_FALSE;
851 }
852
853 STR_SET(obj->message->http.info.request.method, estrndup(method, method_len));
854 RETURN_TRUE;
855 }
856 /* }}} */
857
858 /* {{{ proto string HttpMessage::getRequestUrl()
859 *
860 * Get the Request URL of the Message.
861 *
862 * Returns the request url as string on success, or FALSE if the message
863 * is not of type HttpMessage::TYPE_REQUEST.
864 */
865 PHP_METHOD(HttpMessage, getRequestUrl)
866 {
867 NO_ARGS;
868
869 IF_RETVAL_USED {
870 getObject(http_message_object, obj);
871 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
872 RETURN_STRING(obj->message->http.info.request.url, 1);
873 }
874 }
875 /* }}} */
876
877 /* {{{ proto bool HttpMessage::setRequestUrl(string url)
878 *
879 * Set the Request URL of the HTTP Message.
880 *
881 * Expects a string parameters containing the request url.
882 *
883 * Returns TRUE on success, or FALSE if the message is not of type
884 * HttpMessage::TYPE_REQUEST or supplied URL was empty.
885 */
886 PHP_METHOD(HttpMessage, setRequestUrl)
887 {
888 char *URI;
889 int URIlen;
890 getObject(http_message_object, obj);
891
892 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &URI, &URIlen)) {
893 RETURN_FALSE;
894 }
895 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
896 if (URIlen < 1) {
897 http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestUrl to an empty string");
898 RETURN_FALSE;
899 }
900
901 STR_SET(obj->message->http.info.request.url, estrndup(URI, URIlen));
902 RETURN_TRUE;
903 }
904 /* }}} */
905
906 /* {{{ proto string HttpMessage::getHttpVersion()
907 *
908 * Get the HTTP Protocol Version of the Message.
909 *
910 * Returns the HTTP protocol version as string.
911 */
912 PHP_METHOD(HttpMessage, getHttpVersion)
913 {
914 NO_ARGS;
915
916 IF_RETVAL_USED {
917 char ver[4] = {0};
918 getObject(http_message_object, obj);
919
920 sprintf(ver, "%1.1lf", obj->message->http.version);
921 RETURN_STRINGL(ver, 3, 1);
922 }
923 }
924 /* }}} */
925
926 /* {{{ proto bool HttpMessage::setHttpVersion(string version)
927 *
928 * Set the HTTP Protocol version of the Message.
929 *
930 * Expects a string parameter containing the HTTP protocol version.
931 *
932 * Returns TRUE on success, or FALSE if supplied version is out of range (1.0/1.1).
933 */
934 PHP_METHOD(HttpMessage, setHttpVersion)
935 {
936 char v[4];
937 zval *zv;
938 getObject(http_message_object, obj);
939
940 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &zv)) {
941 return;
942 }
943
944 convert_to_double(zv);
945 sprintf(v, "%1.1lf", Z_DVAL_P(zv));
946 if (strcmp(v, "1.0") && strcmp(v, "1.1")) {
947 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid HTTP protocol version (1.0 or 1.1): %s", v);
948 RETURN_FALSE;
949 }
950
951 obj->message->http.version = Z_DVAL_P(zv);
952 RETURN_TRUE;
953 }
954 /* }}} */
955
956 /* {{{ proto HttpMessage HttpMessage::getParentMessage()
957 *
958 * Get parent Message.
959 *
960 * Returns the parent HttpMessage on success, or NULL if there's none.
961 */
962 PHP_METHOD(HttpMessage, getParentMessage)
963 {
964 NO_ARGS;
965
966 IF_RETVAL_USED {
967 getObject(http_message_object, obj);
968
969 if (obj->message->parent) {
970 RETVAL_OBJVAL(obj->parent);
971 } else {
972 RETVAL_NULL();
973 }
974 }
975 }
976 /* }}} */
977
978 /* {{{ proto bool HttpMessage::send()
979 *
980 * Send the Message according to its type as Response or Request.
981 * This provides limited functionality compared to HttpRequest and HttpResponse.
982 *
983 * Returns TRUE on success, or FALSE on failure.
984 */
985 PHP_METHOD(HttpMessage, send)
986 {
987 getObject(http_message_object, obj);
988
989 NO_ARGS;
990
991 RETURN_SUCCESS(http_message_send(obj->message));
992 }
993 /* }}} */
994
995 /* {{{ proto string HttpMessage::toString([bool include_parent = false])
996 *
997 * Get the string representation of the Message.
998 *
999 * Accepts a bool parameter which specifies whether the returned string
1000 * should also contain any parent messages.
1001 *
1002 * Returns the full message as string.
1003 */
1004 PHP_METHOD(HttpMessage, toString)
1005 {
1006 IF_RETVAL_USED {
1007 char *string;
1008 size_t length;
1009 zend_bool include_parent = 0;
1010 getObject(http_message_object, obj);
1011
1012 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &include_parent)) {
1013 RETURN_FALSE;
1014 }
1015
1016 if (include_parent) {
1017 http_message_serialize(obj->message, &string, &length);
1018 } else {
1019 http_message_tostring(obj->message, &string, &length);
1020 }
1021 RETURN_STRINGL(string, length, 0);
1022 }
1023 }
1024 /* }}} */
1025
1026 /* {{{ proto int HttpMessage::count()
1027 *
1028 * Implements Countable.
1029 *
1030 * Returns the number of parent messages + 1.
1031 */
1032 PHP_METHOD(HttpMessage, count)
1033 {
1034 NO_ARGS {
1035 long i;
1036 http_message *msg;
1037 getObject(http_message_object, obj);
1038
1039 for (i = 0, msg = obj->message; msg; msg = msg->parent, ++i);
1040 RETURN_LONG(i);
1041 }
1042 }
1043 /* }}} */
1044
1045 /* {{{ proto string HttpMessage::serialize()
1046 *
1047 * Implements Serializable.
1048 *
1049 * Returns the serialized representation of the HttpMessage.
1050 */
1051 PHP_METHOD(HttpMessage, serialize)
1052 {
1053 NO_ARGS {
1054 char *string;
1055 size_t length;
1056 getObject(http_message_object, obj);
1057
1058 http_message_serialize(obj->message, &string, &length);
1059 RETURN_STRINGL(string, length, 0);
1060 }
1061 }
1062 /* }}} */
1063
1064 /* {{{ proto void HttpMessage::unserialize(string serialized)
1065 *
1066 * Implements Serializable.
1067 *
1068 * Re-constructs the HttpMessage based upon the serialized string.
1069 */
1070 PHP_METHOD(HttpMessage, unserialize)
1071 {
1072 int length;
1073 char *serialized;
1074 getObject(http_message_object, obj);
1075
1076 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &serialized, &length)) {
1077 http_message_dtor(obj->message);
1078 if (!http_message_parse_ex(obj->message, serialized, (size_t) length)) {
1079 http_error(HE_ERROR, HTTP_E_RUNTIME, "Could not unserialize HttpMessage");
1080 http_message_init(obj->message);
1081 }
1082 }
1083 }
1084 /* }}} */
1085
1086 /* {{{ proto HttpMessage HttpMessage::detach(void)
1087 *
1088 * Returns a clone of an HttpMessage object detached from any parent messages.
1089 */
1090 PHP_METHOD(HttpMessage, detach)
1091 {
1092 http_info info;
1093 http_message *msg;
1094 getObject(http_message_object, obj);
1095
1096 NO_ARGS;
1097
1098 info.type = obj->message->type;
1099 memcpy(&HTTP_INFO(&info), &HTTP_INFO(obj->message), sizeof(struct http_info));
1100
1101 msg = http_message_new();
1102 http_message_set_info(msg, &info);
1103
1104 zend_hash_copy(&msg->hdrs, &obj->message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
1105 phpstr_append(&msg->body, PHPSTR_VAL(obj->message), PHPSTR_LEN(obj->message));
1106
1107 RETURN_OBJVAL(http_message_object_new_ex(Z_OBJCE_P(getThis()), msg, NULL));
1108 }
1109 /* }}} */
1110
1111 /* {{{ proto void HttpMessage::prepend(HttpMessage message)
1112 *
1113 * Prepends message(s) to the HTTP message.
1114 *
1115 * Expects an HttpMessage object as parameter.
1116 */
1117 PHP_METHOD(HttpMessage, prepend)
1118 {
1119 zval *prepend;
1120 zend_bool top = 1;
1121
1122 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|b", &prepend, http_message_object_ce, &top)) {
1123 zval m;
1124 http_message *save_parent_msg;
1125 zend_object_value save_parent_obj;
1126 getObject(http_message_object, obj);
1127 getObjectEx(http_message_object, prepend_obj, prepend);
1128
1129 INIT_PZVAL(&m);
1130 m.type = IS_OBJECT;
1131
1132 if (!top) {
1133 save_parent_obj = obj->parent;
1134 save_parent_msg = obj->message->parent;
1135 } else {
1136 /* iterate to the most parent object */
1137 while (obj->parent.handle) {
1138 m.value.obj = obj->parent;
1139 obj = zend_object_store_get_object(&m TSRMLS_CC);
1140 }
1141 }
1142
1143 /* prepend */
1144 obj->parent = prepend->value.obj;
1145 obj->message->parent = prepend_obj->message;
1146
1147 /* add ref */
1148 zend_objects_store_add_ref(prepend TSRMLS_CC);
1149 while (prepend_obj->parent.handle) {
1150 m.value.obj = prepend_obj->parent;
1151 zend_objects_store_add_ref(&m TSRMLS_CC);
1152 prepend_obj = zend_object_store_get_object(&m TSRMLS_CC);
1153 }
1154
1155 if (!top) {
1156 prepend_obj->parent = save_parent_obj;
1157 prepend_obj->message->parent = save_parent_msg;
1158 }
1159 }
1160 }
1161 /* }}} */
1162
1163 #endif /* ZEND_ENGINE_2 */
1164
1165 /*
1166 * Local variables:
1167 * tab-width: 4
1168 * c-basic-offset: 4
1169 * End:
1170 * vim600: noet sw=4 ts=4 fdm=marker
1171 * vim<600: noet sw=4 ts=4
1172 */
1173