- improve response performance
[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 #define HTTP_WANT_SAPI
16 #define HTTP_WANT_CURL
17 #define HTTP_WANT_MAGIC
18 #include "php_http.h"
19
20 #ifdef ZEND_ENGINE_2
21
22 #include "zend_interfaces.h"
23 #include "ext/standard/url.h"
24
25 #include "php_http_api.h"
26 #include "php_http_send_api.h"
27 #include "php_http_url_api.h"
28 #include "php_http_message_api.h"
29 #include "php_http_message_object.h"
30 #include "php_http_exception_object.h"
31 #include "php_http_response_object.h"
32 #include "php_http_request_method_api.h"
33 #include "php_http_request_api.h"
34 #include "php_http_request_object.h"
35
36 #if defined(HTTP_HAVE_SPL) && !defined(WONKY)
37 /* SPL doesn't install its headers */
38 extern PHPAPI zend_class_entry *spl_ce_Countable;
39 #endif
40
41 #define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpMessage, method, 0, req_args)
42 #define HTTP_EMPTY_ARGS(method) HTTP_EMPTY_ARGS_EX(HttpMessage, method, 0)
43 #define HTTP_MESSAGE_ME(method, visibility) PHP_ME(HttpMessage, method, HTTP_ARGS(HttpMessage, method), visibility)
44
45 HTTP_BEGIN_ARGS(__construct, 0)
46 HTTP_ARG_VAL(message, 0)
47 HTTP_END_ARGS;
48
49 HTTP_BEGIN_ARGS(fromString, 1)
50 HTTP_ARG_VAL(message, 0)
51 HTTP_END_ARGS;
52
53 HTTP_EMPTY_ARGS(getBody);
54 HTTP_BEGIN_ARGS(setBody, 1)
55 HTTP_ARG_VAL(body, 0)
56 HTTP_END_ARGS;
57
58 HTTP_BEGIN_ARGS(getHeader, 1)
59 HTTP_ARG_VAL(header, 0)
60 HTTP_END_ARGS;
61
62 HTTP_EMPTY_ARGS(getHeaders);
63 HTTP_BEGIN_ARGS(setHeaders, 1)
64 HTTP_ARG_VAL(headers, 0)
65 HTTP_END_ARGS;
66
67 HTTP_BEGIN_ARGS(addHeaders, 1)
68 HTTP_ARG_VAL(headers, 0)
69 HTTP_ARG_VAL(append, 0)
70 HTTP_END_ARGS;
71
72 HTTP_EMPTY_ARGS(getType);
73 HTTP_BEGIN_ARGS(setType, 1)
74 HTTP_ARG_VAL(type, 0)
75 HTTP_END_ARGS;
76
77 HTTP_EMPTY_ARGS(getResponseCode);
78 HTTP_BEGIN_ARGS(setResponseCode, 1)
79 HTTP_ARG_VAL(response_code, 0)
80 HTTP_END_ARGS;
81
82 HTTP_EMPTY_ARGS(getResponseStatus);
83 HTTP_BEGIN_ARGS(setResponseStatus, 1)
84 HTTP_ARG_VAL(response_status, 0)
85 HTTP_END_ARGS;
86
87 HTTP_EMPTY_ARGS(getRequestMethod);
88 HTTP_BEGIN_ARGS(setRequestMethod, 1)
89 HTTP_ARG_VAL(request_method, 0)
90 HTTP_END_ARGS;
91
92 HTTP_EMPTY_ARGS(getRequestUrl);
93 HTTP_BEGIN_ARGS(setRequestUrl, 1)
94 HTTP_ARG_VAL(url, 0)
95 HTTP_END_ARGS;
96
97 HTTP_EMPTY_ARGS(getHttpVersion);
98 HTTP_BEGIN_ARGS(setHttpVersion, 1)
99 HTTP_ARG_VAL(http_version, 0)
100 HTTP_END_ARGS;
101
102 HTTP_BEGIN_ARGS(guessContentType, 1)
103 HTTP_ARG_VAL(magic_file, 0)
104 HTTP_ARG_VAL(magic_mode, 0)
105 HTTP_END_ARGS;
106
107 HTTP_EMPTY_ARGS(getParentMessage);
108 HTTP_EMPTY_ARGS(send);
109 HTTP_BEGIN_ARGS(toString, 0)
110 HTTP_ARG_VAL(include_parent, 0)
111 HTTP_END_ARGS;
112
113 HTTP_EMPTY_ARGS(toMessageTypeObject);
114
115 HTTP_EMPTY_ARGS(count);
116
117 HTTP_EMPTY_ARGS(serialize);
118 HTTP_BEGIN_ARGS(unserialize, 1)
119 HTTP_ARG_VAL(serialized, 0)
120 HTTP_END_ARGS;
121
122 HTTP_EMPTY_ARGS(rewind);
123 HTTP_EMPTY_ARGS(valid);
124 HTTP_EMPTY_ARGS(key);
125 HTTP_EMPTY_ARGS(current);
126 HTTP_EMPTY_ARGS(next);
127
128 HTTP_EMPTY_ARGS(detach);
129 HTTP_BEGIN_ARGS(prepend, 1)
130 HTTP_ARG_OBJ(HttpMessage, message, 0)
131 HTTP_END_ARGS;
132 HTTP_EMPTY_ARGS(reverse);
133
134 #define http_message_object_read_prop _http_message_object_read_prop
135 static zval *_http_message_object_read_prop(zval *object, zval *member, int type TSRMLS_DC);
136 #define http_message_object_write_prop _http_message_object_write_prop
137 static void _http_message_object_write_prop(zval *object, zval *member, zval *value TSRMLS_DC);
138 #define http_message_object_get_props _http_message_object_get_props
139 static HashTable *_http_message_object_get_props(zval *object TSRMLS_DC);
140
141 #define OBJ_PROP_CE http_message_object_ce
142 zend_class_entry *http_message_object_ce;
143 zend_function_entry http_message_object_fe[] = {
144 HTTP_MESSAGE_ME(__construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
145 HTTP_MESSAGE_ME(getBody, ZEND_ACC_PUBLIC)
146 HTTP_MESSAGE_ME(setBody, ZEND_ACC_PUBLIC)
147 HTTP_MESSAGE_ME(getHeader, ZEND_ACC_PUBLIC)
148 HTTP_MESSAGE_ME(getHeaders, ZEND_ACC_PUBLIC)
149 HTTP_MESSAGE_ME(setHeaders, ZEND_ACC_PUBLIC)
150 HTTP_MESSAGE_ME(addHeaders, ZEND_ACC_PUBLIC)
151 HTTP_MESSAGE_ME(getType, ZEND_ACC_PUBLIC)
152 HTTP_MESSAGE_ME(setType, ZEND_ACC_PUBLIC)
153 HTTP_MESSAGE_ME(getResponseCode, ZEND_ACC_PUBLIC)
154 HTTP_MESSAGE_ME(setResponseCode, ZEND_ACC_PUBLIC)
155 HTTP_MESSAGE_ME(getResponseStatus, ZEND_ACC_PUBLIC)
156 HTTP_MESSAGE_ME(setResponseStatus, ZEND_ACC_PUBLIC)
157 HTTP_MESSAGE_ME(getRequestMethod, ZEND_ACC_PUBLIC)
158 HTTP_MESSAGE_ME(setRequestMethod, ZEND_ACC_PUBLIC)
159 HTTP_MESSAGE_ME(getRequestUrl, ZEND_ACC_PUBLIC)
160 HTTP_MESSAGE_ME(setRequestUrl, ZEND_ACC_PUBLIC)
161 HTTP_MESSAGE_ME(getHttpVersion, ZEND_ACC_PUBLIC)
162 HTTP_MESSAGE_ME(setHttpVersion, ZEND_ACC_PUBLIC)
163 HTTP_MESSAGE_ME(guessContentType, ZEND_ACC_PUBLIC)
164 HTTP_MESSAGE_ME(getParentMessage, ZEND_ACC_PUBLIC)
165 HTTP_MESSAGE_ME(send, ZEND_ACC_PUBLIC)
166 HTTP_MESSAGE_ME(toString, ZEND_ACC_PUBLIC)
167 HTTP_MESSAGE_ME(toMessageTypeObject, ZEND_ACC_PUBLIC)
168
169 /* implements Countable */
170 HTTP_MESSAGE_ME(count, ZEND_ACC_PUBLIC)
171
172 /* implements Serializable */
173 HTTP_MESSAGE_ME(serialize, ZEND_ACC_PUBLIC)
174 HTTP_MESSAGE_ME(unserialize, ZEND_ACC_PUBLIC)
175
176 /* implements Iterator */
177 HTTP_MESSAGE_ME(rewind, ZEND_ACC_PUBLIC)
178 HTTP_MESSAGE_ME(valid, ZEND_ACC_PUBLIC)
179 HTTP_MESSAGE_ME(current, ZEND_ACC_PUBLIC)
180 HTTP_MESSAGE_ME(key, ZEND_ACC_PUBLIC)
181 HTTP_MESSAGE_ME(next, ZEND_ACC_PUBLIC)
182
183 ZEND_MALIAS(HttpMessage, __toString, toString, HTTP_ARGS(HttpMessage, toString), ZEND_ACC_PUBLIC)
184
185 HTTP_MESSAGE_ME(fromString, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
186
187 HTTP_MESSAGE_ME(detach, ZEND_ACC_PUBLIC)
188 HTTP_MESSAGE_ME(prepend, ZEND_ACC_PUBLIC)
189 HTTP_MESSAGE_ME(reverse, ZEND_ACC_PUBLIC)
190
191 EMPTY_FUNCTION_ENTRY
192 };
193 static zend_object_handlers http_message_object_handlers;
194
195 PHP_MINIT_FUNCTION(http_message_object)
196 {
197 HTTP_REGISTER_CLASS_EX(HttpMessage, http_message_object, NULL, 0);
198
199 #ifndef WONKY
200 # ifdef HTTP_HAVE_SPL
201 zend_class_implements(http_message_object_ce TSRMLS_CC, 3, spl_ce_Countable, zend_ce_serializable, zend_ce_iterator);
202 # else
203 zend_class_implements(http_message_object_ce TSRMLS_CC, 2, zend_ce_serializable, zend_ce_iterator);
204 # endif
205 #else
206 zend_class_implements(http_message_object_ce TSRMLS_CC, 1, zend_ce_iterator);
207 #endif
208
209 http_message_object_handlers.clone_obj = _http_message_object_clone_obj;
210 http_message_object_handlers.read_property = http_message_object_read_prop;
211 http_message_object_handlers.write_property = http_message_object_write_prop;
212 http_message_object_handlers.get_properties = http_message_object_get_props;
213 http_message_object_handlers.get_property_ptr_ptr = NULL;
214
215 DCL_PROP(PROTECTED, long, type, HTTP_MSG_NONE);
216 DCL_PROP(PROTECTED, string, body, "");
217 DCL_PROP(PROTECTED, string, requestMethod, "");
218 DCL_PROP(PROTECTED, string, requestUrl, "");
219 DCL_PROP(PROTECTED, string, responseStatus, "");
220 DCL_PROP(PROTECTED, long, responseCode, 0);
221 DCL_PROP_N(PROTECTED, httpVersion);
222 DCL_PROP_N(PROTECTED, headers);
223 DCL_PROP_N(PROTECTED, parentMessage);
224
225 #ifndef WONKY
226 DCL_CONST(long, "TYPE_NONE", HTTP_MSG_NONE);
227 DCL_CONST(long, "TYPE_REQUEST", HTTP_MSG_REQUEST);
228 DCL_CONST(long, "TYPE_RESPONSE", HTTP_MSG_RESPONSE);
229 #endif
230
231 HTTP_LONG_CONSTANT("HTTP_MSG_NONE", HTTP_MSG_NONE);
232 HTTP_LONG_CONSTANT("HTTP_MSG_REQUEST", HTTP_MSG_REQUEST);
233 HTTP_LONG_CONSTANT("HTTP_MSG_RESPONSE", HTTP_MSG_RESPONSE);
234
235 return SUCCESS;
236 }
237
238 void _http_message_object_reverse(zval *this_ptr, zval *return_value TSRMLS_DC)
239 {
240 int i;
241 getObject(http_message_object, obj);
242
243 /* count */
244 http_message_count(i, obj->message);
245
246 if (i > 1) {
247 zval o;
248 zend_object_value *ovalues = NULL;
249 http_message_object **objects = NULL;
250 int last = i - 1;
251
252 objects = ecalloc(i, sizeof(http_message_object *));
253 ovalues = ecalloc(i, sizeof(zend_object_value));
254
255 /* we are the first message */
256 objects[0] = obj;
257 ovalues[0] = getThis()->value.obj;
258
259 /* fetch parents */
260 INIT_PZVAL(&o);
261 o.type = IS_OBJECT;
262 for (i = 1; obj->parent.handle; ++i) {
263 o.value.obj = obj->parent;
264 ovalues[i] = o.value.obj;
265 objects[i] = obj = zend_object_store_get_object(&o TSRMLS_CC);
266 }
267
268 /* reorder parents */
269 for (last = --i; i; --i) {
270 objects[i]->message->parent = objects[i-1]->message;
271 objects[i]->parent = ovalues[i-1];
272 }
273 objects[0]->message->parent = NULL;
274 objects[0]->parent.handle = 0;
275 objects[0]->parent.handlers = NULL;
276
277 /* add ref (why?) */
278 Z_OBJ_ADDREF_P(getThis());
279 RETVAL_OBJVAL(ovalues[last], 1);
280
281 efree(objects);
282 efree(ovalues);
283 } else {
284 RETURN_ZVAL(getThis(), 1, 0);
285 }
286 }
287
288 void _http_message_object_prepend_ex(zval *this_ptr, zval *prepend, zend_bool top TSRMLS_DC)
289 {
290 zval m;
291 http_message *save_parent_msg = NULL;
292 zend_object_value save_parent_obj = {0, NULL};
293 getObject(http_message_object, obj);
294 getObjectEx(http_message_object, prepend_obj, prepend);
295
296 INIT_PZVAL(&m);
297 m.type = IS_OBJECT;
298
299 if (!top) {
300 save_parent_obj = obj->parent;
301 save_parent_msg = obj->message->parent;
302 } else {
303 /* iterate to the most parent object */
304 while (obj->parent.handle) {
305 m.value.obj = obj->parent;
306 obj = zend_object_store_get_object(&m TSRMLS_CC);
307 }
308 }
309
310 /* prepend */
311 obj->parent = prepend->value.obj;
312 obj->message->parent = prepend_obj->message;
313
314 /* add ref */
315 zend_objects_store_add_ref(prepend TSRMLS_CC);
316 while (prepend_obj->parent.handle) {
317 m.value.obj = prepend_obj->parent;
318 zend_objects_store_add_ref(&m TSRMLS_CC);
319 prepend_obj = zend_object_store_get_object(&m TSRMLS_CC);
320 }
321
322 if (!top) {
323 prepend_obj->parent = save_parent_obj;
324 prepend_obj->message->parent = save_parent_msg;
325 }
326 }
327
328 zend_object_value _http_message_object_new(zend_class_entry *ce TSRMLS_DC)
329 {
330 return http_message_object_new_ex(ce, NULL, NULL);
331 }
332
333 zend_object_value _http_message_object_new_ex(zend_class_entry *ce, http_message *msg, http_message_object **ptr TSRMLS_DC)
334 {
335 zend_object_value ov;
336 http_message_object *o;
337
338 o = ecalloc(1, sizeof(http_message_object));
339 o->zo.ce = ce;
340
341 if (ptr) {
342 *ptr = o;
343 }
344
345 if (msg) {
346 o->message = msg;
347 if (msg->parent) {
348 o->parent = http_message_object_new_ex(ce, msg->parent, NULL);
349 }
350 }
351
352 ALLOC_HASHTABLE(OBJ_PROP(o));
353 zend_hash_init(OBJ_PROP(o), zend_hash_num_elements(&ce->default_properties), NULL, ZVAL_PTR_DTOR, 0);
354 zend_hash_copy(OBJ_PROP(o), &ce->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
355
356 ov.handle = putObject(http_message_object, o);
357 ov.handlers = &http_message_object_handlers;
358
359 return ov;
360 }
361
362 zend_object_value _http_message_object_clone_obj(zval *this_ptr TSRMLS_DC)
363 {
364 zend_object_value new_ov;
365 http_message_object *new_obj = NULL;
366 getObject(http_message_object, old_obj);
367
368 new_ov = http_message_object_new_ex(old_obj->zo.ce, http_message_dup(old_obj->message), &new_obj);
369 zend_objects_clone_members(&new_obj->zo, new_ov, &old_obj->zo, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC);
370
371 return new_ov;
372 }
373
374 void _http_message_object_free(zend_object *object TSRMLS_DC)
375 {
376 http_message_object *o = (http_message_object *) object;
377
378 if (o->message) {
379 http_message_dtor(o->message);
380 efree(o->message);
381 }
382 if (o->parent.handle) {
383 zval p;
384
385 INIT_PZVAL(&p);
386 p.type = IS_OBJECT;
387 p.value.obj = o->parent;
388 zend_objects_store_del_ref(&p TSRMLS_CC);
389 }
390 freeObject(o);
391 }
392
393 static zval *_http_message_object_read_prop(zval *object, zval *member, int type TSRMLS_DC)
394 {
395 getObjectEx(http_message_object, obj, object);
396 http_message *msg = obj->message;
397 zval *return_value;
398 #ifdef WONKY
399 ulong h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member)+1);
400 #else
401 zend_property_info *pinfo = zend_get_property_info(obj->zo.ce, member, 1 TSRMLS_CC);
402
403 if (!pinfo || ACC_PROP_PUBLIC(pinfo->flags)) {
404 return zend_get_std_object_handlers()->read_property(object, member, type TSRMLS_CC);
405 }
406 #endif
407
408 if (type == BP_VAR_W) {
409 zend_error(E_ERROR, "Cannot access HttpMessage properties by reference or array key/index");
410 return NULL;
411 }
412
413 ALLOC_ZVAL(return_value);
414 return_value->refcount = 0;
415 return_value->is_ref = 0;
416
417 #ifdef WONKY
418 switch (h)
419 #else
420 switch (pinfo->h)
421 #endif
422 {
423 case HTTP_MSG_PROPHASH_TYPE:
424 case HTTP_MSG_CHILD_PROPHASH_TYPE:
425 RETVAL_LONG(msg->type);
426 break;
427
428 case HTTP_MSG_PROPHASH_HTTP_VERSION:
429 case HTTP_MSG_CHILD_PROPHASH_HTTP_VERSION:
430 RETVAL_DOUBLE(msg->http.version);
431 break;
432
433 case HTTP_MSG_PROPHASH_BODY:
434 case HTTP_MSG_CHILD_PROPHASH_BODY:
435 phpstr_fix(PHPSTR(msg));
436 RETVAL_PHPSTR(PHPSTR(msg), 0, 1);
437 break;
438
439 case HTTP_MSG_PROPHASH_HEADERS:
440 case HTTP_MSG_CHILD_PROPHASH_HEADERS:
441 array_init(return_value);
442 zend_hash_copy(Z_ARRVAL_P(return_value), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
443 break;
444
445 case HTTP_MSG_PROPHASH_PARENT_MESSAGE:
446 case HTTP_MSG_CHILD_PROPHASH_PARENT_MESSAGE:
447 if (msg->parent) {
448 RETVAL_OBJVAL(obj->parent, 1);
449 } else {
450 RETVAL_NULL();
451 }
452 break;
453
454 case HTTP_MSG_PROPHASH_REQUEST_METHOD:
455 case HTTP_MSG_CHILD_PROPHASH_REQUEST_METHOD:
456 if (HTTP_MSG_TYPE(REQUEST, msg) && msg->http.info.request.method) {
457 RETVAL_STRING(msg->http.info.request.method, 1);
458 } else {
459 RETVAL_NULL();
460 }
461 break;
462
463 case HTTP_MSG_PROPHASH_REQUEST_URL:
464 case HTTP_MSG_CHILD_PROPHASH_REQUEST_URL:
465 if (HTTP_MSG_TYPE(REQUEST, msg) && msg->http.info.request.url) {
466 RETVAL_STRING(msg->http.info.request.url, 1);
467 } else {
468 RETVAL_NULL();
469 }
470 break;
471
472 case HTTP_MSG_PROPHASH_RESPONSE_CODE:
473 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_CODE:
474 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
475 RETVAL_LONG(msg->http.info.response.code);
476 } else {
477 RETVAL_NULL();
478 }
479 break;
480
481 case HTTP_MSG_PROPHASH_RESPONSE_STATUS:
482 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_STATUS:
483 if (HTTP_MSG_TYPE(RESPONSE, msg) && msg->http.info.response.status) {
484 RETVAL_STRING(msg->http.info.response.status, 1);
485 } else {
486 RETVAL_NULL();
487 }
488 break;
489
490 default:
491 #ifdef WONKY
492 return zend_get_std_object_handlers()->read_property(object, member, type TSRMLS_CC);
493 #else
494 RETVAL_NULL();
495 #endif
496 }
497
498 return return_value;
499 }
500
501 static void _http_message_object_write_prop(zval *object, zval *member, zval *value TSRMLS_DC)
502 {
503 getObjectEx(http_message_object, obj, object);
504 http_message *msg = obj->message;
505 zval *cpy = NULL;
506 #ifdef WONKY
507 ulong h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1);
508 #else
509 zend_property_info *pinfo = zend_get_property_info(obj->zo.ce, member, 1 TSRMLS_CC);
510
511 if (!pinfo || ACC_PROP_PUBLIC(pinfo->flags)) {
512 zend_get_std_object_handlers()->write_property(object, member, value TSRMLS_CC);
513 return;
514 }
515 #endif
516
517 cpy = zval_copy(Z_TYPE_P(value), value);
518
519 #ifdef WONKY
520 switch (h)
521 #else
522 switch (pinfo->h)
523 #endif
524 {
525 case HTTP_MSG_PROPHASH_TYPE:
526 case HTTP_MSG_CHILD_PROPHASH_TYPE:
527 convert_to_long(cpy);
528 http_message_set_type(msg, Z_LVAL_P(cpy));
529 break;
530
531 case HTTP_MSG_PROPHASH_HTTP_VERSION:
532 case HTTP_MSG_CHILD_PROPHASH_HTTP_VERSION:
533 convert_to_double(cpy);
534 msg->http.version = Z_DVAL_P(cpy);
535 break;
536
537 case HTTP_MSG_PROPHASH_BODY:
538 case HTTP_MSG_CHILD_PROPHASH_BODY:
539 convert_to_string(cpy);
540 phpstr_dtor(PHPSTR(msg));
541 phpstr_from_string_ex(PHPSTR(msg), Z_STRVAL_P(cpy), Z_STRLEN_P(cpy));
542 break;
543
544 case HTTP_MSG_PROPHASH_HEADERS:
545 case HTTP_MSG_CHILD_PROPHASH_HEADERS:
546 convert_to_array(cpy);
547 zend_hash_clean(&msg->hdrs);
548 zend_hash_copy(&msg->hdrs, Z_ARRVAL_P(cpy), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
549 break;
550
551 case HTTP_MSG_PROPHASH_PARENT_MESSAGE:
552 case HTTP_MSG_CHILD_PROPHASH_PARENT_MESSAGE:
553 if (Z_TYPE_P(value) == IS_OBJECT && instanceof_function(Z_OBJCE_P(value), http_message_object_ce TSRMLS_CC)) {
554 if (msg->parent) {
555 zval tmp;
556 tmp.value.obj = obj->parent;
557 Z_OBJ_DELREF(tmp);
558 }
559 Z_OBJ_ADDREF_P(value);
560 obj->parent = value->value.obj;
561 }
562 break;
563
564 case HTTP_MSG_PROPHASH_REQUEST_METHOD:
565 case HTTP_MSG_CHILD_PROPHASH_REQUEST_METHOD:
566 if (HTTP_MSG_TYPE(REQUEST, msg)) {
567 convert_to_string(cpy);
568 STR_SET(msg->http.info.request.method, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
569 }
570 break;
571
572 case HTTP_MSG_PROPHASH_REQUEST_URL:
573 case HTTP_MSG_CHILD_PROPHASH_REQUEST_URL:
574 if (HTTP_MSG_TYPE(REQUEST, msg)) {
575 convert_to_string(cpy);
576 STR_SET(msg->http.info.request.url, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
577 }
578 break;
579
580 case HTTP_MSG_PROPHASH_RESPONSE_CODE:
581 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_CODE:
582 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
583 convert_to_long(cpy);
584 msg->http.info.response.code = Z_LVAL_P(cpy);
585 }
586 break;
587
588 case HTTP_MSG_PROPHASH_RESPONSE_STATUS:
589 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_STATUS:
590 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
591 convert_to_string(cpy);
592 STR_SET(msg->http.info.response.status, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
593 }
594 break;
595
596 default:
597 #ifdef WONKY
598 zend_get_std_object_handlers()->write_property(object, member, value TSRMLS_CC);
599 #endif
600 break;
601 }
602 zval_free(&cpy);
603 }
604
605 static HashTable *_http_message_object_get_props(zval *object TSRMLS_DC)
606 {
607 zval *headers;
608 getObjectEx(http_message_object, obj, object);
609 http_message *msg = obj->message;
610 HashTable *props = OBJ_PROP(obj);
611 zval array, *parent;
612
613 INIT_ZARR(array, props);
614
615 #define ASSOC_PROP(array, ptype, name, val) \
616 { \
617 char *m_prop_name; \
618 int m_prop_len; \
619 zend_mangle_property_name(&m_prop_name, &m_prop_len, "*", 1, name, lenof(name), 0); \
620 add_assoc_ ##ptype## _ex(&array, m_prop_name, sizeof(name)+3, val); \
621 efree(m_prop_name); \
622 }
623 #define ASSOC_STRING(array, name, val) ASSOC_STRINGL(array, name, val, strlen(val))
624 #define ASSOC_STRINGL(array, name, val, len) \
625 { \
626 char *m_prop_name; \
627 int m_prop_len; \
628 zend_mangle_property_name(&m_prop_name, &m_prop_len, "*", 1, name, lenof(name), 0); \
629 add_assoc_stringl_ex(&array, m_prop_name, sizeof(name)+3, val, len, 1); \
630 efree(m_prop_name); \
631 }
632
633 ASSOC_PROP(array, long, "type", msg->type);
634 ASSOC_PROP(array, double, "httpVersion", msg->http.version);
635
636 switch (msg->type) {
637 case HTTP_MSG_REQUEST:
638 ASSOC_PROP(array, long, "responseCode", 0);
639 ASSOC_STRINGL(array, "responseStatus", "", 0);
640 ASSOC_STRING(array, "requestMethod", STR_PTR(msg->http.info.request.method));
641 ASSOC_STRING(array, "requestUrl", STR_PTR(msg->http.info.request.url));
642 break;
643
644 case HTTP_MSG_RESPONSE:
645 ASSOC_PROP(array, long, "responseCode", msg->http.info.response.code);
646 ASSOC_STRING(array, "responseStatus", STR_PTR(msg->http.info.response.status));
647 ASSOC_STRINGL(array, "requestMethod", "", 0);
648 ASSOC_STRINGL(array, "requestUrl", "", 0);
649 break;
650
651 case HTTP_MSG_NONE:
652 default:
653 ASSOC_PROP(array, long, "responseCode", 0);
654 ASSOC_STRINGL(array, "responseStatus", "", 0);
655 ASSOC_STRINGL(array, "requestMethod", "", 0);
656 ASSOC_STRINGL(array, "requestUrl", "", 0);
657 break;
658 }
659
660 MAKE_STD_ZVAL(headers);
661 array_init(headers);
662 zend_hash_copy(Z_ARRVAL_P(headers), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
663 ASSOC_PROP(array, zval, "headers", headers);
664 ASSOC_STRINGL(array, "body", PHPSTR_VAL(msg), PHPSTR_LEN(msg));
665
666 MAKE_STD_ZVAL(parent);
667 if (msg->parent) {
668 ZVAL_OBJVAL(parent, obj->parent, 1);
669 } else {
670 ZVAL_NULL(parent);
671 }
672 ASSOC_PROP(array, zval, "parentMessage", parent);
673
674 return OBJ_PROP(obj);
675 }
676
677 /* ### USERLAND ### */
678
679 /* {{{ proto void HttpMessage::__construct([string message])
680 *
681 * Instantiate a new HttpMessage object.
682 *
683 * Accepts an optional string parameter containing a single or several
684 * consecutive HTTP messages. The constructed object will actually
685 * represent the *last* message of the passed string. If there were
686 * prior messages, those can be accessed by HttpMessage::getParentMessage().
687 *
688 * Throws HttpMalformedHeaderException.
689 */
690 PHP_METHOD(HttpMessage, __construct)
691 {
692 int length = 0;
693 char *message = NULL;
694
695 getObject(http_message_object, obj);
696
697 SET_EH_THROW_HTTP();
698 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &message, &length) && message && length) {
699 http_message *msg = obj->message;
700
701 http_message_dtor(msg);
702 if ((obj->message = http_message_parse_ex(msg, message, length))) {
703 if (obj->message->parent) {
704 obj->parent = http_message_object_new_ex(Z_OBJCE_P(getThis()), obj->message->parent, NULL);
705 }
706 } else {
707 obj->message = http_message_init(msg);
708 }
709 }
710 if (!obj->message) {
711 obj->message = http_message_new();
712 }
713 SET_EH_NORMAL();
714 }
715 /* }}} */
716
717 /* {{{ proto static HttpMessage HttpMessage::fromString(string raw_message[, string class_name = "HttpMessage"])
718 *
719 * Create an HttpMessage object from a string. Kind of a static constructor.
720 *
721 * Expects a string parameter containing a single or several consecutive
722 * HTTP messages. Accepts an optional string parameter specifying the class to use.
723 *
724 * Returns an HttpMessage object on success or NULL on failure.
725 *
726 * Throws HttpMalformedHeadersException.
727 */
728 PHP_METHOD(HttpMessage, fromString)
729 {
730 char *string = NULL, *class_name = NULL;
731 int length = 0, class_length = 0;
732 http_message *msg = NULL;
733
734 RETVAL_NULL();
735
736 SET_EH_THROW_HTTP();
737 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &string, &length, &class_name, &class_length)) {
738 if ((msg = http_message_parse(string, length))) {
739 zend_class_entry *ce = http_message_object_ce;
740
741 if (class_name && *class_name) {
742 ce = zend_fetch_class(class_name, class_length, ZEND_FETCH_CLASS_DEFAULT TSRMLS_CC);
743 if (ce && !instanceof_function(ce, http_message_object_ce TSRMLS_CC)) {
744 http_error_ex(HE_WARNING, HTTP_E_RUNTIME, "Class %s does not extend HttpMessage", class_name);
745 ce = NULL;
746 }
747 }
748 if (ce) {
749 RETVAL_OBJVAL(http_message_object_new_ex(ce, msg, NULL), 0);
750 }
751 }
752 }
753 SET_EH_NORMAL();
754 }
755 /* }}} */
756
757 /* {{{ proto string HttpMessage::getBody()
758 *
759 * Get the body of the parsed HttpMessage.
760 *
761 * Returns the message body as string.
762 */
763 PHP_METHOD(HttpMessage, getBody)
764 {
765 NO_ARGS;
766
767 if (return_value_used) {
768 getObject(http_message_object, obj);
769 RETURN_PHPSTR(&obj->message->body, PHPSTR_FREE_NOT, 1);
770 }
771 }
772 /* }}} */
773
774 /* {{{ proto void HttpMessage::setBody(string body)
775 *
776 * Set the body of the HttpMessage.
777 * NOTE: Don't forget to update any headers accordingly.
778 *
779 * Expects a string parameter containing the new body of the message.
780 */
781 PHP_METHOD(HttpMessage, setBody)
782 {
783 char *body;
784 int len;
785 getObject(http_message_object, obj);
786
787 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &body, &len)) {
788 phpstr_dtor(PHPSTR(obj->message));
789 phpstr_from_string_ex(PHPSTR(obj->message), body, len);
790 }
791 }
792 /* }}} */
793
794 /* {{{ proto string HttpMessage::getHeader(string header)
795 *
796 * Get message header.
797 *
798 * Returns the header value on success or NULL if the header does not exist.
799 */
800 PHP_METHOD(HttpMessage, getHeader)
801 {
802 zval *header;
803 char *orig_header, *nice_header;
804 int header_len;
805 getObject(http_message_object, obj);
806
807 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &orig_header, &header_len)) {
808 RETURN_FALSE;
809 }
810
811 nice_header = pretty_key(estrndup(orig_header, header_len), header_len, 1, 1);
812 if ((header = http_message_header_ex(obj->message, nice_header, header_len + 1, 0))) {
813 RETVAL_ZVAL(header, 1, 1);
814 }
815 efree(nice_header);
816 }
817 /* }}} */
818
819 /* {{{ proto array HttpMessage::getHeaders()
820 *
821 * Get Message Headers.
822 *
823 * Returns an associative array containing the messages HTTP headers.
824 */
825 PHP_METHOD(HttpMessage, getHeaders)
826 {
827 NO_ARGS;
828
829 if (return_value_used) {
830 getObject(http_message_object, obj);
831
832 array_init(return_value);
833 array_copy(&obj->message->hdrs, Z_ARRVAL_P(return_value));
834 }
835 }
836 /* }}} */
837
838 /* {{{ proto void HttpMessage::setHeaders(array headers)
839 *
840 * Sets new headers.
841 *
842 * Expects an associative array as parameter containing the new HTTP headers,
843 * which will replace *all* previous HTTP headers of the message.
844 */
845 PHP_METHOD(HttpMessage, setHeaders)
846 {
847 zval *new_headers = NULL;
848 getObject(http_message_object, obj);
849
850 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/!", &new_headers)) {
851 return;
852 }
853
854 zend_hash_clean(&obj->message->hdrs);
855 if (new_headers) {
856 array_copy(Z_ARRVAL_P(new_headers), &obj->message->hdrs);
857 }
858 }
859 /* }}} */
860
861 /* {{{ proto void HttpMessage::addHeaders(array headers[, bool append = false])
862 *
863 * Add headers. If append is true, headers with the same name will be separated, else overwritten.
864 *
865 * Expects an associative array as parameter containing the additional HTTP headers
866 * to add to the messages existing headers. If the optional bool parameter is true,
867 * and a header with the same name of one to add exists already, this respective
868 * header will be converted to an array containing both header values, otherwise
869 * it will be overwritten with the new header value.
870 */
871 PHP_METHOD(HttpMessage, addHeaders)
872 {
873 zval *new_headers;
874 zend_bool append = 0;
875 getObject(http_message_object, obj);
876
877 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &new_headers, &append)) {
878 return;
879 }
880
881 array_join(Z_ARRVAL_P(new_headers), &obj->message->hdrs, append, ARRAY_JOIN_STRONLY|ARRAY_JOIN_PRETTIFY);
882 }
883 /* }}} */
884
885 /* {{{ proto int HttpMessage::getType()
886 *
887 * Get Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE)
888 *
889 * Returns the HttpMessage::TYPE.
890 */
891 PHP_METHOD(HttpMessage, getType)
892 {
893 NO_ARGS;
894
895 if (return_value_used) {
896 getObject(http_message_object, obj);
897 RETURN_LONG(obj->message->type);
898 }
899 }
900 /* }}} */
901
902 /* {{{ proto void HttpMessage::setType(int type)
903 *
904 * Set Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE)
905 *
906 * Expects an int parameter, the HttpMessage::TYPE.
907 */
908 PHP_METHOD(HttpMessage, setType)
909 {
910 long type;
911 getObject(http_message_object, obj);
912
913 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type)) {
914 return;
915 }
916 http_message_set_type(obj->message, type);
917 }
918 /* }}} */
919
920 /* {{{ proto int HttpMessage::getResponseCode()
921 *
922 * Get the Response Code of the Message.
923 *
924 * Returns the HTTP response code if the message is of type
925 * HttpMessage::TYPE_RESPONSE, else FALSE.
926 */
927 PHP_METHOD(HttpMessage, getResponseCode)
928 {
929 NO_ARGS;
930
931 if (return_value_used) {
932 getObject(http_message_object, obj);
933 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
934 RETURN_LONG(obj->message->http.info.response.code);
935 }
936 }
937 /* }}} */
938
939 /* {{{ proto bool HttpMessage::setResponseCode(int code)
940 *
941 * Set the response code of an HTTP Response Message.
942 *
943 * Expects an int parameter with the HTTP response code.
944 *
945 * Returns TRUE on success, or FALSE if the message is not of type
946 * HttpMessage::TYPE_RESPONSE or the response code is out of range (100-510).
947 */
948 PHP_METHOD(HttpMessage, setResponseCode)
949 {
950 long code;
951 getObject(http_message_object, obj);
952
953 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
954
955 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) {
956 RETURN_FALSE;
957 }
958 if (code < 100 || code > 510) {
959 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid response code (100-510): %ld", code);
960 RETURN_FALSE;
961 }
962
963 obj->message->http.info.response.code = code;
964 RETURN_TRUE;
965 }
966 /* }}} */
967
968 /* {{{ proto string HttpMessage::getResponseStatus()
969 *
970 * Get the Response Status of the message (i.e. the string following the response code).
971 *
972 * Returns the HTTP response status string if the message is of type
973 * HttpMessage::TYPE_RESPONSE, else FALSE.
974 */
975 PHP_METHOD(HttpMessage, getResponseStatus)
976 {
977 NO_ARGS;
978
979 if (return_value_used) {
980 getObject(http_message_object, obj);
981 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
982 RETURN_STRING(obj->message->http.info.response.status, 1);
983 }
984 }
985 /* }}} */
986
987 /* {{{ proto bool HttpMessage::setResponseStatus(string status)
988 *
989 * Set the Response Status of the HTTP message (i.e. the string following the response code).
990 *
991 * Expects a string parameter containing the response status text.
992 *
993 * Returns TRUE on success or FALSE if the message is not of type
994 * HttpMessage::TYPE_RESPONSE.
995 */
996 PHP_METHOD(HttpMessage, setResponseStatus)
997 {
998 char *status;
999 int status_len;
1000 getObject(http_message_object, obj);
1001
1002 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
1003
1004 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &status, &status_len)) {
1005 RETURN_FALSE;
1006 }
1007 STR_SET(obj->message->http.info.response.status, estrdup(status));
1008 RETURN_TRUE;
1009 }
1010 /* }}} */
1011
1012 /* {{{ proto string HttpMessage::getRequestMethod()
1013 *
1014 * Get the Request Method of the Message.
1015 *
1016 * Returns the request method name on success, or FALSE if the message is
1017 * not of type HttpMessage::TYPE_REQUEST.
1018 */
1019 PHP_METHOD(HttpMessage, getRequestMethod)
1020 {
1021 NO_ARGS;
1022
1023 if (return_value_used) {
1024 getObject(http_message_object, obj);
1025 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
1026 RETURN_STRING(obj->message->http.info.request.method, 1);
1027 }
1028 }
1029 /* }}} */
1030
1031 /* {{{ proto bool HttpMessage::setRequestMethod(string method)
1032 *
1033 * Set the Request Method of the HTTP Message.
1034 *
1035 * Expects a string parameter containing the request method name.
1036 *
1037 * Returns TRUE on success, or FALSE if the message is not of type
1038 * HttpMessage::TYPE_REQUEST or an invalid request method was supplied.
1039 */
1040 PHP_METHOD(HttpMessage, setRequestMethod)
1041 {
1042 char *method;
1043 int method_len;
1044 getObject(http_message_object, obj);
1045
1046 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
1047
1048 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len)) {
1049 RETURN_FALSE;
1050 }
1051 if (method_len < 1) {
1052 http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestMethod to an empty string");
1053 RETURN_FALSE;
1054 }
1055 if (!http_request_method_exists(1, 0, method)) {
1056 http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Unknown request method: %s", method);
1057 RETURN_FALSE;
1058 }
1059
1060 STR_SET(obj->message->http.info.request.method, estrndup(method, method_len));
1061 RETURN_TRUE;
1062 }
1063 /* }}} */
1064
1065 /* {{{ proto string HttpMessage::getRequestUrl()
1066 *
1067 * Get the Request URL of the Message.
1068 *
1069 * Returns the request url as string on success, or FALSE if the message
1070 * is not of type HttpMessage::TYPE_REQUEST.
1071 */
1072 PHP_METHOD(HttpMessage, getRequestUrl)
1073 {
1074 NO_ARGS;
1075
1076 if (return_value_used) {
1077 getObject(http_message_object, obj);
1078 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
1079 RETURN_STRING(obj->message->http.info.request.url, 1);
1080 }
1081 }
1082 /* }}} */
1083
1084 /* {{{ proto bool HttpMessage::setRequestUrl(string url)
1085 *
1086 * Set the Request URL of the HTTP Message.
1087 *
1088 * Expects a string parameters containing the request url.
1089 *
1090 * Returns TRUE on success, or FALSE if the message is not of type
1091 * HttpMessage::TYPE_REQUEST or supplied URL was empty.
1092 */
1093 PHP_METHOD(HttpMessage, setRequestUrl)
1094 {
1095 char *URI;
1096 int URIlen;
1097 getObject(http_message_object, obj);
1098
1099 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &URI, &URIlen)) {
1100 RETURN_FALSE;
1101 }
1102 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
1103 if (URIlen < 1) {
1104 http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestUrl to an empty string");
1105 RETURN_FALSE;
1106 }
1107
1108 STR_SET(obj->message->http.info.request.url, estrndup(URI, URIlen));
1109 RETURN_TRUE;
1110 }
1111 /* }}} */
1112
1113 /* {{{ proto string HttpMessage::getHttpVersion()
1114 *
1115 * Get the HTTP Protocol Version of the Message.
1116 *
1117 * Returns the HTTP protocol version as string.
1118 */
1119 PHP_METHOD(HttpMessage, getHttpVersion)
1120 {
1121 NO_ARGS;
1122
1123 if (return_value_used) {
1124 char ver[4] = {0};
1125 getObject(http_message_object, obj);
1126
1127 sprintf(ver, "%1.1lf", obj->message->http.version);
1128 RETURN_STRINGL(ver, 3, 1);
1129 }
1130 }
1131 /* }}} */
1132
1133 /* {{{ proto bool HttpMessage::setHttpVersion(string version)
1134 *
1135 * Set the HTTP Protocol version of the Message.
1136 *
1137 * Expects a string parameter containing the HTTP protocol version.
1138 *
1139 * Returns TRUE on success, or FALSE if supplied version is out of range (1.0/1.1).
1140 */
1141 PHP_METHOD(HttpMessage, setHttpVersion)
1142 {
1143 char v[4];
1144 zval *zv;
1145 getObject(http_message_object, obj);
1146
1147 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &zv)) {
1148 return;
1149 }
1150
1151 convert_to_double(zv);
1152 sprintf(v, "%1.1lf", Z_DVAL_P(zv));
1153 if (strcmp(v, "1.0") && strcmp(v, "1.1")) {
1154 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid HTTP protocol version (1.0 or 1.1): %s", v);
1155 RETURN_FALSE;
1156 }
1157
1158 obj->message->http.version = Z_DVAL_P(zv);
1159 RETURN_TRUE;
1160 }
1161 /* }}} */
1162
1163 /* {{{ proto string HttpMessage::guessContentType(string magic_file[, int magic_mode = MAGIC_MIME])
1164 *
1165 * Attempts to guess the content type of supplied payload through libmagic.
1166 *
1167 * Expects a string parameter specifying the magic.mime database to use.
1168 * Additionally accepts an optional int parameter, being flags for libmagic.
1169 *
1170 * Returns the guessed content type on success, or FALSE on failure.
1171 *
1172 * Throws HttpRuntimeException, HttpInvalidParamException
1173 * if http.only_exceptions is TRUE.
1174 */
1175 PHP_METHOD(HttpMessage, guessContentType)
1176 {
1177 #ifdef HTTP_HAVE_MAGIC
1178 char *magic_file, *ct = NULL;
1179 int magic_file_len;
1180 long magic_mode = MAGIC_MIME;
1181
1182 RETVAL_FALSE;
1183 SET_EH_THROW_HTTP();
1184 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &magic_file, &magic_file_len, &magic_mode)) {
1185 getObject(http_message_object, obj);
1186 if ((ct = http_guess_content_type(magic_file, magic_mode, PHPSTR_VAL(&obj->message->body), PHPSTR_LEN(&obj->message->body), SEND_DATA))) {
1187 RETVAL_STRING(ct, 0);
1188 }
1189 }
1190 SET_EH_NORMAL();
1191 #else
1192 http_error(HE_THROW, HTTP_E_RUNTIME, "Cannot guess Content-Type; libmagic not available");
1193 RETURN_FALSE;
1194 #endif
1195 }
1196 /* }}} */
1197
1198 /* {{{ proto HttpMessage HttpMessage::getParentMessage()
1199 *
1200 * Get parent Message.
1201 *
1202 * Returns the parent HttpMessage on success, or NULL if there's none.
1203 *
1204 * Throws HttpRuntimeException.
1205 */
1206 PHP_METHOD(HttpMessage, getParentMessage)
1207 {
1208 SET_EH_THROW_HTTP();
1209 NO_ARGS {
1210 getObject(http_message_object, obj);
1211
1212 if (obj->message->parent) {
1213 RETVAL_OBJVAL(obj->parent, 1);
1214 } else {
1215 http_error(HE_WARNING, HTTP_E_RUNTIME, "HttpMessage does not have a parent message");
1216 }
1217 }
1218 SET_EH_NORMAL();
1219 }
1220 /* }}} */
1221
1222 /* {{{ proto bool HttpMessage::send()
1223 *
1224 * Send the Message according to its type as Response or Request.
1225 * This provides limited functionality compared to HttpRequest and HttpResponse.
1226 *
1227 * Returns TRUE on success, or FALSE on failure.
1228 */
1229 PHP_METHOD(HttpMessage, send)
1230 {
1231 getObject(http_message_object, obj);
1232
1233 NO_ARGS;
1234
1235 RETURN_SUCCESS(http_message_send(obj->message));
1236 }
1237 /* }}} */
1238
1239 /* {{{ proto string HttpMessage::toString([bool include_parent = false])
1240 *
1241 * Get the string representation of the Message.
1242 *
1243 * Accepts a bool parameter which specifies whether the returned string
1244 * should also contain any parent messages.
1245 *
1246 * Returns the full message as string.
1247 */
1248 PHP_METHOD(HttpMessage, toString)
1249 {
1250 if (return_value_used) {
1251 char *string;
1252 size_t length;
1253 zend_bool include_parent = 0;
1254 getObject(http_message_object, obj);
1255
1256 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &include_parent)) {
1257 RETURN_FALSE;
1258 }
1259
1260 if (include_parent) {
1261 http_message_serialize(obj->message, &string, &length);
1262 } else {
1263 http_message_tostring(obj->message, &string, &length);
1264 }
1265 RETURN_STRINGL(string, length, 0);
1266 }
1267 }
1268 /* }}} */
1269
1270 /* {{{ proto HttpRequest|HttpResponse HttpMessage::toMessageTypeObject(void)
1271 *
1272 * Creates an object regarding to the type of the message.
1273 *
1274 * Returns either an HttpRequest or HttpResponse object on success, or NULL on failure.
1275 *
1276 * Throws HttpRuntimeException, HttpMessageTypeException, HttpHeaderException.
1277 */
1278 PHP_METHOD(HttpMessage, toMessageTypeObject)
1279 {
1280 SET_EH_THROW_HTTP();
1281
1282 NO_ARGS;
1283
1284 if (return_value_used) {
1285 getObject(http_message_object, obj);
1286
1287 switch (obj->message->type) {
1288 case HTTP_MSG_REQUEST:
1289 {
1290 #ifdef HTTP_HAVE_CURL
1291 int method;
1292 char *url;
1293 zval body, *array, *headers, *host = http_message_header(obj->message, "Host");
1294 php_url hurl, *purl = php_url_parse(obj->message->http.info.request.url);
1295
1296 MAKE_STD_ZVAL(array);
1297 array_init(array);
1298
1299 memset(&hurl, 0, sizeof(php_url));
1300 hurl.host = host ? Z_STRVAL_P(host) : NULL;
1301 zval_ptr_dtor(&host);
1302 http_build_url(HTTP_URL_REPLACE, purl, &hurl, NULL, &url, NULL);
1303 php_url_free(purl);
1304 add_assoc_string(array, "url", url, 0);
1305
1306 if ( (method = http_request_method_exists(1, 0, obj->message->http.info.request.method)) ||
1307 (method = http_request_method_register(obj->message->http.info.request.method, strlen(obj->message->http.info.request.method)))) {
1308 add_assoc_long(array, "method", method);
1309 }
1310
1311 if (10 == (int) (obj->message->http.version * 10)) {
1312 add_assoc_long(array, "protocol", CURL_HTTP_VERSION_1_0);
1313 }
1314
1315 MAKE_STD_ZVAL(headers);
1316 array_init(headers);
1317 array_copy(&obj->message->hdrs, Z_ARRVAL_P(headers));
1318 add_assoc_zval(array, "headers", headers);
1319
1320 object_init_ex(return_value, http_request_object_ce);
1321 zend_call_method_with_1_params(&return_value, http_request_object_ce, NULL, "setoptions", NULL, array);
1322 zval_ptr_dtor(&array);
1323
1324 INIT_PZVAL(&body);
1325 ZVAL_STRINGL(&body, PHPSTR_VAL(obj->message), PHPSTR_LEN(obj->message), 0);
1326 zend_call_method_with_1_params(&return_value, http_request_object_ce, NULL, "setrawpostdata", NULL, &body);
1327 #else
1328 http_error(HE_WARNING, HTTP_E_RUNTIME, "Cannot transform HttpMessage to HttpRequest (missing curl support)");
1329 #endif
1330 break;
1331 }
1332
1333 case HTTP_MSG_RESPONSE:
1334 {
1335 #ifndef WONKY
1336 HashPosition pos1, pos2;
1337 HashKey key = initHashKey(0);
1338 zval **header, **h, *body;
1339
1340 if (obj->message->http.info.response.code) {
1341 http_send_status(obj->message->http.info.response.code);
1342 }
1343
1344 object_init_ex(return_value, http_response_object_ce);
1345
1346 FOREACH_HASH_KEYVAL(pos1, &obj->message->hdrs, key, header) {
1347 if (key.type == HASH_KEY_IS_STRING) {
1348 zval *zkey;
1349
1350 MAKE_STD_ZVAL(zkey);
1351 ZVAL_STRINGL(zkey, key.str, key.len - 1, 1);
1352
1353 switch (Z_TYPE_PP(header)) {
1354 case IS_ARRAY:
1355 case IS_OBJECT:
1356 FOREACH_HASH_VAL(pos2, HASH_OF(*header), h) {
1357 ZVAL_ADDREF(*h);
1358 zend_call_method_with_2_params(&return_value, http_response_object_ce, NULL, "setheader", NULL, zkey, *h);
1359 zval_ptr_dtor(h);
1360 }
1361 break;
1362
1363 default:
1364 ZVAL_ADDREF(*header);
1365 zend_call_method_with_2_params(&return_value, http_response_object_ce, NULL, "setheader", NULL, zkey, *header);
1366 zval_ptr_dtor(header);
1367 break;
1368 }
1369 zval_ptr_dtor(&zkey);
1370 }
1371 }
1372
1373 MAKE_STD_ZVAL(body);
1374 ZVAL_STRINGL(body, PHPSTR_VAL(obj->message), PHPSTR_LEN(obj->message), 1);
1375 zend_call_method_with_1_params(&return_value, http_response_object_ce, NULL, "setdata", NULL, body);
1376 zval_ptr_dtor(&body);
1377 #else
1378 http_error(HE_WARNING, HTTP_E_RUNTIME, "Cannot transform HttpMessage to HttpResponse (need PHP 5.1+)");
1379 #endif
1380 break;
1381 }
1382
1383 default:
1384 http_error(HE_WARNING, HTTP_E_MESSAGE_TYPE, "HttpMessage is neither of type HttpMessage::TYPE_REQUEST nor HttpMessage::TYPE_RESPONSE");
1385 break;
1386 }
1387 }
1388 SET_EH_NORMAL();
1389 }
1390 /* }}} */
1391
1392 /* {{{ proto int HttpMessage::count()
1393 *
1394 * Implements Countable.
1395 *
1396 * Returns the number of parent messages + 1.
1397 */
1398 PHP_METHOD(HttpMessage, count)
1399 {
1400 NO_ARGS {
1401 long i;
1402 getObject(http_message_object, obj);
1403
1404 http_message_count(i, obj->message);
1405 RETURN_LONG(i);
1406 }
1407 }
1408 /* }}} */
1409
1410 /* {{{ proto string HttpMessage::serialize()
1411 *
1412 * Implements Serializable.
1413 *
1414 * Returns the serialized representation of the HttpMessage.
1415 */
1416 PHP_METHOD(HttpMessage, serialize)
1417 {
1418 NO_ARGS {
1419 char *string;
1420 size_t length;
1421 getObject(http_message_object, obj);
1422
1423 http_message_serialize(obj->message, &string, &length);
1424 RETURN_STRINGL(string, length, 0);
1425 }
1426 }
1427 /* }}} */
1428
1429 /* {{{ proto void HttpMessage::unserialize(string serialized)
1430 *
1431 * Implements Serializable.
1432 *
1433 * Re-constructs the HttpMessage based upon the serialized string.
1434 */
1435 PHP_METHOD(HttpMessage, unserialize)
1436 {
1437 int length;
1438 char *serialized;
1439 getObject(http_message_object, obj);
1440
1441 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &serialized, &length)) {
1442 http_message_dtor(obj->message);
1443 if (!http_message_parse_ex(obj->message, serialized, (size_t) length)) {
1444 http_error(HE_ERROR, HTTP_E_RUNTIME, "Could not unserialize HttpMessage");
1445 http_message_init(obj->message);
1446 }
1447 }
1448 }
1449 /* }}} */
1450
1451 /* {{{ proto HttpMessage HttpMessage::detach(void)
1452 *
1453 * Returns a clone of an HttpMessage object detached from any parent messages.
1454 */
1455 PHP_METHOD(HttpMessage, detach)
1456 {
1457 http_info info;
1458 http_message *msg;
1459 getObject(http_message_object, obj);
1460
1461 NO_ARGS;
1462
1463 info.type = obj->message->type;
1464 memcpy(&HTTP_INFO(&info), &HTTP_INFO(obj->message), sizeof(struct http_info));
1465
1466 msg = http_message_new();
1467 http_message_set_info(msg, &info);
1468
1469 zend_hash_copy(&msg->hdrs, &obj->message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
1470 phpstr_append(&msg->body, PHPSTR_VAL(obj->message), PHPSTR_LEN(obj->message));
1471
1472 RETVAL_OBJVAL(http_message_object_new_ex(Z_OBJCE_P(getThis()), msg, NULL), 0);
1473 }
1474 /* }}} */
1475
1476 /* {{{ proto void HttpMessage::prepend(HttpMessage message[, bool top = true])
1477 *
1478 * Prepends message(s) to the HTTP message.
1479 *
1480 * Expects an HttpMessage object as parameter.
1481 *
1482 * Throws HttpInvalidParamException if the message is located within the same message chain.
1483 */
1484 PHP_METHOD(HttpMessage, prepend)
1485 {
1486 zval *prepend;
1487 zend_bool top = 1;
1488
1489 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|b", &prepend, http_message_object_ce, &top)) {
1490 http_message *msg[2];
1491 getObject(http_message_object, obj);
1492 getObjectEx(http_message_object, prepend_obj, prepend);
1493
1494 /* safety check */
1495 for (msg[0] = obj->message; msg[0]; msg[0] = msg[0]->parent) {
1496 for (msg[1] = prepend_obj->message; msg[1]; msg[1] = msg[1]->parent) {
1497 if (msg[0] == msg[1]) {
1498 http_error(HE_THROW, HTTP_E_INVALID_PARAM, "Cannot prepend a message located within the same message chain");
1499 return;
1500 }
1501 }
1502 }
1503
1504 http_message_object_prepend_ex(getThis(), prepend, top);
1505 }
1506 }
1507 /* }}} */
1508
1509 /* {{{ proto HttpMessage HttpMessage::reverse()
1510 *
1511 * Reorders the message chain in reverse order.
1512 *
1513 * Returns the most parent HttpMessage object.
1514 */
1515 PHP_METHOD(HttpMessage, reverse)
1516 {
1517 NO_ARGS {
1518 http_message_object_reverse(getThis(), return_value);
1519 }
1520 }
1521 /* }}} */
1522
1523 /* {{{ proto void HttpMessage::rewind(void)
1524 *
1525 * Implements Iterator.
1526 */
1527 PHP_METHOD(HttpMessage, rewind)
1528 {
1529 NO_ARGS {
1530 getObject(http_message_object, obj);
1531
1532 if (obj->iterator) {
1533 zval_ptr_dtor(&obj->iterator);
1534 }
1535 ZVAL_ADDREF(getThis());
1536 obj->iterator = getThis();
1537 }
1538 }
1539 /* }}} */
1540
1541 /* {{{ proto bool HttpMessage::valid(void)
1542 *
1543 * Implements Iterator.
1544 */
1545 PHP_METHOD(HttpMessage, valid)
1546 {
1547 NO_ARGS {
1548 getObject(http_message_object, obj);
1549
1550 RETURN_BOOL(obj->iterator != NULL);
1551 }
1552 }
1553 /* }}} */
1554
1555 /* {{{ proto void HttpMessage::next(void)
1556 *
1557 * Implements Iterator.
1558 */
1559 PHP_METHOD(HttpMessage, next)
1560 {
1561 NO_ARGS {
1562 getObject(http_message_object, obj);
1563 getObjectEx(http_message_object, itr, obj->iterator);
1564
1565 if (itr && itr->parent.handle) {
1566 zval *old = obj->iterator;
1567 MAKE_STD_ZVAL(obj->iterator);
1568 ZVAL_OBJVAL(obj->iterator, itr->parent, 1);
1569 zval_ptr_dtor(&old);
1570 } else {
1571 zval_ptr_dtor(&obj->iterator);
1572 obj->iterator = NULL;
1573 }
1574 }
1575 }
1576 /* }}} */
1577
1578 /* {{{ proto int HttpMessage::key(void)
1579 *
1580 * Implements Iterator.
1581 */
1582 PHP_METHOD(HttpMessage, key)
1583 {
1584 NO_ARGS {
1585 getObject(http_message_object, obj);
1586
1587 RETURN_LONG(obj->iterator ? obj->iterator->value.obj.handle:0);
1588 }
1589 }
1590 /* }}} */
1591
1592 /* {{{ proto HttpMessage HttpMessage::current(void)
1593 *
1594 * Implements Iterator.
1595 */
1596 PHP_METHOD(HttpMessage, current)
1597 {
1598 NO_ARGS {
1599 getObject(http_message_object, obj);
1600
1601 if (obj->iterator) {
1602 RETURN_ZVAL(obj->iterator, 1, 0);
1603 }
1604 }
1605 }
1606 /* }}} */
1607
1608 #endif /* ZEND_ENGINE_2 */
1609
1610 /*
1611 * Local variables:
1612 * tab-width: 4
1613 * c-basic-offset: 4
1614 * End:
1615 * vim600: noet sw=4 ts=4 fdm=marker
1616 * vim<600: noet sw=4 ts=4
1617 */
1618