From 2a76e8214572645ccdecb20c7b6ad24b65a626b1 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Fri, 20 Jan 2006 14:23:08 +0000 Subject: [PATCH] - add HttpMessage::toMessageTypeObject() - add Iterator interface to HttpMessage --- http_message_api.c | 11 +- http_message_object.c | 250 ++++++++++++++++++++++++++++++++++++- php_http_message_object.h | 7 ++ tests/HttpMessage_001.phpt | 4 + tests/HttpMessage_004.phpt | 36 ++++++ tests/HttpMessage_005.phpt | 86 +++++++++++++ tests/HttpMessage_006.phpt | 35 ++++++ tests/etag_mode_031.phpt | 2 +- tests/etag_mode_041.phpt | 2 +- 9 files changed, 420 insertions(+), 13 deletions(-) create mode 100644 tests/HttpMessage_004.phpt create mode 100644 tests/HttpMessage_005.phpt create mode 100644 tests/HttpMessage_006.phpt diff --git a/http_message_api.c b/http_message_api.c index 27f3d78..baeedcc 100644 --- a/http_message_api.c +++ b/http_message_api.c @@ -108,19 +108,20 @@ PHP_HTTP_API void _http_message_set_info(http_message *message, http_info *info) { message->http.version = info->http.version; - switch (info->type) + switch (message->type = info->type) { case IS_HTTP_REQUEST: - message->type = HTTP_MSG_REQUEST; HTTP_INFO(message).request.url = estrdup(HTTP_INFO(info).request.url); STR_SET(HTTP_INFO(message).request.method, estrdup(HTTP_INFO(info).request.method)); - break; + break; case IS_HTTP_RESPONSE: - message->type = HTTP_MSG_RESPONSE; HTTP_INFO(message).response.code = HTTP_INFO(info).response.code; STR_SET(HTTP_INFO(message).response.status, estrdup(HTTP_INFO(info).response.status)); - break; + break; + + default: + break; } } diff --git a/http_message_object.c b/http_message_object.c index 7d4530d..00fb3ce 100644 --- a/http_message_object.c +++ b/http_message_object.c @@ -12,18 +12,28 @@ /* $Id$ */ +#define HTTP_WANT_SAPI +#define HTTP_WANT_CURL #include "php_http.h" #ifdef ZEND_ENGINE_2 +#include "ext/standard/url.h" + #include "php_http_api.h" +#include "php_http_send_api.h" +#include "php_http_url_api.h" #include "php_http_message_api.h" #include "php_http_message_object.h" #include "php_http_exception_object.h" +#include "php_http_response_object.h" +#include "php_http_request_method_api.h" +#include "php_http_request_api.h" +#include "php_http_request_object.h" #ifndef WONKY # include "zend_interfaces.h" -# if defined(HAVE_SPL) +# ifdef HAVE_SPL /* SPL doesn't install its headers */ extern PHPAPI zend_class_entry *spl_ce_Countable; # endif @@ -87,6 +97,8 @@ HTTP_BEGIN_ARGS(toString, 0, 0) HTTP_ARG_VAL(include_parent, 0) HTTP_END_ARGS; +HTTP_EMPTY_ARGS(toMessageTypeObject, 0); + HTTP_EMPTY_ARGS(count, 0); HTTP_EMPTY_ARGS(serialize, 0); @@ -94,8 +106,13 @@ HTTP_BEGIN_ARGS(unserialize, 0, 1) HTTP_ARG_VAL(serialized, 0) HTTP_END_ARGS; -HTTP_EMPTY_ARGS(detach, 0); +HTTP_EMPTY_ARGS(rewind, 0); +HTTP_EMPTY_ARGS(valid, 0); +HTTP_EMPTY_ARGS(key, 0); +HTTP_EMPTY_ARGS(current, 0); +HTTP_EMPTY_ARGS(next, 0); +HTTP_EMPTY_ARGS(detach, 0); HTTP_BEGIN_ARGS(prepend, 0, 1) HTTP_ARG_OBJ(HttpMessage, message, 0) HTTP_END_ARGS; @@ -130,6 +147,7 @@ zend_function_entry http_message_object_fe[] = { HTTP_MESSAGE_ME(getParentMessage, ZEND_ACC_PUBLIC) HTTP_MESSAGE_ME(send, ZEND_ACC_PUBLIC) HTTP_MESSAGE_ME(toString, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(toMessageTypeObject, ZEND_ACC_PUBLIC) /* implements Countable */ HTTP_MESSAGE_ME(count, ZEND_ACC_PUBLIC) @@ -138,14 +156,20 @@ zend_function_entry http_message_object_fe[] = { HTTP_MESSAGE_ME(serialize, ZEND_ACC_PUBLIC) HTTP_MESSAGE_ME(unserialize, ZEND_ACC_PUBLIC) + /* implements Iterator */ + HTTP_MESSAGE_ME(rewind, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(valid, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(current, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(key, ZEND_ACC_PUBLIC) + HTTP_MESSAGE_ME(next, ZEND_ACC_PUBLIC) + ZEND_MALIAS(HttpMessage, __toString, toString, HTTP_ARGS(HttpMessage, toString), ZEND_ACC_PUBLIC) HTTP_MESSAGE_ME(fromString, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) HTTP_MESSAGE_ME(detach, ZEND_ACC_PUBLIC) - HTTP_MESSAGE_ME(prepend, ZEND_ACC_PUBLIC) - + EMPTY_FUNCTION_ENTRY }; static zend_object_handlers http_message_object_handlers; @@ -155,10 +179,12 @@ PHP_MINIT_FUNCTION(http_message_object) HTTP_REGISTER_CLASS_EX(HttpMessage, http_message_object, NULL, 0); #ifndef WONKY # ifdef HAVE_SPL - zend_class_implements(http_message_object_ce TSRMLS_CC, 2, spl_ce_Countable, zend_ce_serializable); + zend_class_implements(http_message_object_ce TSRMLS_CC, 3, spl_ce_Countable, zend_ce_serializable, zend_ce_iterator); # else - zend_class_implements(http_message_object_ce TSRMLS_CC, 1, zend_ce_serializable); + zend_class_implements(http_message_object_ce TSRMLS_CC, 2, zend_ce_serializable, zend_ce_iterator); # endif +#else + zend_class_implements(http_message_object_ce TSRMLS_CC, 1, zend_ce_iterator); #endif HTTP_LONG_CONSTANT("HTTP_MSG_NONE", HTTP_MSG_NONE); @@ -1027,6 +1053,132 @@ PHP_METHOD(HttpMessage, toString) } /* }}} */ +/* {{{ proto HttpRequest|HttpResponse HttpMessage::toMessageTypeObject(void) + * + * Creates an object regarding to the type of the message. + * + * Returns either an HttpRequest or HttpResponse object on success, or NULL on failure. + * + * Throws HttpRuntimeException, HttpMessageTypeException, HttpHeaderException. + */ +PHP_METHOD(HttpMessage, toMessageTypeObject) +{ + SET_EH_THROW_HTTP(); + + NO_ARGS; + + IF_RETVAL_USED { + getObject(http_message_object, obj); + + switch (obj->message->type) + { + case HTTP_MSG_REQUEST: + { +#ifdef HTTP_HAVE_CURL + int method; + char *url; + zval tmp, body, *array, *headers, *host = http_message_header(obj->message, "Host"); + php_url hurl, *purl = php_url_parse(obj->message->http.info.request.url); + + MAKE_STD_ZVAL(array); + array_init(array); + + memset(&hurl, 0, sizeof(php_url)); + hurl.host = host ? Z_STRVAL_P(host) : NULL; + http_build_url(purl, &hurl, NULL, &url, NULL); + php_url_free(purl); + add_assoc_string(array, "url", url, 0); + + if ( (method = http_request_method_exists(1, 0, obj->message->http.info.request.method)) || + (method = http_request_method_register(obj->message->http.info.request.method, strlen(obj->message->http.info.request.method)))) { + add_assoc_long(array, "method", method); + } + + if (10 == (int) (obj->message->http.version * 10)) { + add_assoc_long(array, "protocol", CURL_HTTP_VERSION_1_0); + } + + MAKE_STD_ZVAL(headers); + array_init(headers); + INIT_ZARR(tmp, &obj->message->hdrs); + array_copy(&tmp, headers); + add_assoc_zval(array, "headers", headers); + + object_init_ex(return_value, http_request_object_ce); + zend_call_method_with_1_params(&return_value, http_request_object_ce, NULL, "setoptions", NULL, array); + zval_ptr_dtor(&array); + + INIT_PZVAL(&body); + ZVAL_STRINGL(&body, PHPSTR_VAL(obj->message), PHPSTR_LEN(obj->message), 0); + zend_call_method_with_1_params(&return_value, http_request_object_ce, NULL, "setrawpostdata", NULL, &body); +#else + http_error(HE_WARNING, HTTP_E_RUNTIME, "Cannot transform HttpMessage to HttpRequest (missing curl support)"); +#endif + } + break; + + case HTTP_MSG_RESPONSE: + { +#ifndef WONKY + HashPosition pos1, pos2; + ulong idx; + uint key_len; + char *key = NULL; + zval **header, **h, *body; + + if (obj->message->http.info.response.code) { + http_send_status(obj->message->http.info.response.code); + } + + object_init_ex(return_value, http_response_object_ce); + + FOREACH_HASH_KEYLENVAL(pos1, &obj->message->hdrs, key, key_len, idx, header) { + if (key) { + zval zkey; + + INIT_PZVAL(&zkey); + ZVAL_STRINGL(&zkey, key, key_len, 0); + + switch (Z_TYPE_PP(header)) + { + case IS_ARRAY: + case IS_OBJECT: + FOREACH_HASH_VAL(pos2, HASH_OF(*header), h) { + ZVAL_ADDREF(*h); + zend_call_method_with_2_params(&return_value, http_response_object_ce, NULL, "setheader", NULL, &zkey, *h); + zval_ptr_dtor(h); + } + break; + + default: + ZVAL_ADDREF(*header); + zend_call_method_with_2_params(&return_value, http_response_object_ce, NULL, "setheader", NULL, &zkey, *header); + zval_ptr_dtor(header); + break; + } + key = NULL; + } + } + + MAKE_STD_ZVAL(body); + ZVAL_STRINGL(body, PHPSTR_VAL(obj->message), PHPSTR_LEN(obj->message), 1); + zend_call_method_with_1_params(&return_value, http_response_object_ce, NULL, "setdata", NULL, body); + zval_ptr_dtor(&body); +#else + http_error(HE_WARNING, HTTP_E_RUNTIME, "Cannot transform HttpMessage to HttpResponse (need PHP 5.1+)"); +#endif + } + break; + + default: + http_error(HE_WARNING, HTTP_E_MESSAGE_TYPE, "HttpMessage is neither of type HttpMessage::TYPE_REQUEST nor HttpMessage::TYPE_RESPONSE"); + break; + } + } + SET_EH_NORMAL(); +} +/* }}} */ + /* {{{ proto int HttpMessage::count() * * Implements Countable. @@ -1164,6 +1316,92 @@ PHP_METHOD(HttpMessage, prepend) } /* }}} */ +/* {{{ proto void HttpMessage::rewind(void) + * + * Implements Iterator. + */ +PHP_METHOD(HttpMessage, rewind) +{ + NO_ARGS { + getObject(http_message_object, obj); + + if (obj->iterator) { + zval_ptr_dtor(&obj->iterator); + } + ZVAL_ADDREF(getThis()); + obj->iterator = getThis(); + } +} +/* }}} */ + +/* {{{ proto bool HttpMessage::valid(void) + * + * Implements Iterator. + */ +PHP_METHOD(HttpMessage, valid) +{ + NO_ARGS { + getObject(http_message_object, obj); + + RETURN_BOOL(obj->iterator != NULL); + } +} +/* }}} */ + +/* {{{ proto void HttpMessage::next(void) + * + * Implements Iterator. + */ +PHP_METHOD(HttpMessage, next) +{ + NO_ARGS { + getObject(http_message_object, obj); + getObjectEx(http_message_object, itr, obj->iterator); + + if (itr && itr->parent.handle) { + zval *old = obj->iterator; + MAKE_STD_ZVAL(obj->iterator); + ZVAL_OBJVAL(obj->iterator, itr->parent); + Z_OBJ_ADDREF_P(obj->iterator); + zval_ptr_dtor(&old); + } else { + zval_ptr_dtor(&obj->iterator); + obj->iterator = NULL; + } + } +} +/* }}} */ + +/* {{{ proto int HttpMessage::key(void) + * + * Implements Iterator. + */ +PHP_METHOD(HttpMessage, key) +{ + NO_ARGS { + getObject(http_message_object, obj); + + RETURN_LONG(obj->iterator ? obj->iterator->value.obj.handle:0); + } +} +/* }}} */ + +/* {{{ proto HttpMessage HttpMessage::current(void) + * + * Implements Iterator. + */ +PHP_METHOD(HttpMessage, current) +{ + NO_ARGS { + getObject(http_message_object, obj); + + if (obj->iterator) { + RETURN_ZVAL(obj->iterator, 1, 0); + } + } +} +/* }}} */ + #endif /* ZEND_ENGINE_2 */ /* diff --git a/php_http_message_object.h b/php_http_message_object.h index 8b28b15..97f8214 100644 --- a/php_http_message_object.h +++ b/php_http_message_object.h @@ -20,6 +20,7 @@ typedef struct { zend_object zo; http_message *message; zend_object_value parent; + zval *iterator; } http_message_object; extern zend_class_entry *http_message_object_ce; @@ -88,10 +89,16 @@ PHP_METHOD(HttpMessage, setHttpVersion); PHP_METHOD(HttpMessage, getParentMessage); PHP_METHOD(HttpMessage, send); PHP_METHOD(HttpMessage, toString); +PHP_METHOD(HttpMessage, toMessageTypeObject); PHP_METHOD(HttpMessage, count); PHP_METHOD(HttpMessage, serialize); PHP_METHOD(HttpMessage, unserialize); +PHP_METHOD(HttpMessage, rewind); +PHP_METHOD(HttpMessage, valid); +PHP_METHOD(HttpMessage, current); +PHP_METHOD(HttpMessage, key); +PHP_METHOD(HttpMessage, next); PHP_METHOD(HttpMessage, fromString); diff --git a/tests/HttpMessage_001.phpt b/tests/HttpMessage_001.phpt index 6791bc3..25adb8f 100644 --- a/tests/HttpMessage_001.phpt +++ b/tests/HttpMessage_001.phpt @@ -22,6 +22,10 @@ $m = new HttpMessage( "00" ); +$x = $m->getParentMessage(); +$x = $m->getParentMessage(); +$x = $m->getParentMessage(); + var_dump($m->getBody()); var_dump(HttpMessage::fromString($m->toString(true))->toString(true)); do { diff --git a/tests/HttpMessage_004.phpt b/tests/HttpMessage_004.phpt new file mode 100644 index 0000000..ab0ede9 --- /dev/null +++ b/tests/HttpMessage_004.phpt @@ -0,0 +1,36 @@ +--TEST-- +HttpMessage::detach() +--SKIPIF-- + +--FILE-- +detach(); +$d->addHeaders(array('Server'=>'Funky/2.0')); +var_dump($d->getHeaders() == $m->getHeaders()); +var_dump($d->getBody()); + +echo "Done\n"; +?> +--EXPECTF-- +%sTEST +bool(false) +string(3) "Hi!" +Done \ No newline at end of file diff --git a/tests/HttpMessage_005.phpt b/tests/HttpMessage_005.phpt new file mode 100644 index 0000000..6466d6e --- /dev/null +++ b/tests/HttpMessage_005.phpt @@ -0,0 +1,86 @@ +--TEST-- +HttpMessage::prepend() +--SKIPIF-- + +--FILE-- +prepend($m2); +$m2 = NULL; +echo $m1->toString(true); + +$m1->prepend($m1->detach(), false); +echo $m1->toString(true); + +echo "Done\n"; +?> +--EXPECTF-- +%sTEST +GET http://example.com/ HTTP/1.0 +HTTP/1.1 200 ok +Server: Funky/2.0 +Content-Type: text/html +Content-Length: 9 + +Hi there! +GET / HTTP/1.1 +Host: example.com +Accept: */* +Connection: close +HTTP/1.1 200 ok +Server: Funky/1.0 +Content-Type: text/plain +Content-Length: 3 + +Hi! +GET http://example.com/ HTTP/1.0 +HTTP/1.1 200 ok +Server: Funky/2.0 +Content-Type: text/html +Content-Length: 9 + +Hi there! +GET / HTTP/1.1 +Host: example.com +Accept: */* +Connection: close +HTTP/1.1 200 ok +Server: Funky/1.0 +Content-Type: text/plain +Content-Length: 3 + +Hi! +HTTP/1.1 200 ok +Server: Funky/1.0 +Content-Type: text/plain +Content-Length: 3 + +Hi! +Done \ No newline at end of file diff --git a/tests/HttpMessage_006.phpt b/tests/HttpMessage_006.phpt new file mode 100644 index 0000000..e1e236d --- /dev/null +++ b/tests/HttpMessage_006.phpt @@ -0,0 +1,35 @@ +--TEST-- +HttpMessage iterator +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%sTEST +== +HTTP/1.1 304 Not Modified +== +GET /foo HTTP/1.1 +== +HTTP/1.1 200 OK +== +GET / HTTP/1.1 +Done \ No newline at end of file diff --git a/tests/etag_mode_031.phpt b/tests/etag_mode_031.phpt index e4cd2d4..a7d02f1 100644 --- a/tests/etag_mode_031.phpt +++ b/tests/etag_mode_031.phpt @@ -8,7 +8,7 @@ checkmin(5.1); ?> --FILE-- diff --git a/tests/etag_mode_041.phpt b/tests/etag_mode_041.phpt index b71cdb6..e367de8 100644 --- a/tests/etag_mode_041.phpt +++ b/tests/etag_mode_041.phpt @@ -8,7 +8,7 @@ checkmin(5.1); ?> --FILE-- -- 2.30.2