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