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