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