X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=blobdiff_plain;f=http_request_object.c;h=2ed979789b009c8a93b2c983baca769b5a50a946;hp=0a6c5d1a4ea47df32b7f84a8708e2199ab6bcaae;hb=34644fd4c700fb55c3a2c2ff9966d0f5e7572509;hpb=36fa8b05be7aaf8e7f9e503c3e82733022500c03 diff --git a/http_request_object.c b/http_request_object.c index 0a6c5d1..2ed9797 100644 --- a/http_request_object.c +++ b/http_request_object.c @@ -1,16 +1,13 @@ /* - +----------------------------------------------------------------------+ - | PECL :: http | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.0 of the PHP license, that | - | is bundled with this package in the file LICENSE, and is available | - | through the world-wide-web at http://www.php.net/license/3_0.txt. | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Copyright (c) 2004-2005 Michael Wallner | - +----------------------------------------------------------------------+ + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2005, Michael Wallner | + +--------------------------------------------------------------------+ */ /* $Id$ */ @@ -23,6 +20,8 @@ #if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL) +#include "zend_interfaces.h" + #include "php_http_std_defs.h" #include "php_http_request_object.h" #include "php_http_request_api.h" @@ -52,6 +51,7 @@ HTTP_EMPTY_ARGS(__destruct, 0); HTTP_BEGIN_ARGS(__construct, 0, 0) HTTP_ARG_VAL(url, 0) HTTP_ARG_VAL(method, 0) + HTTP_ARG_VAL(options, 0) HTTP_END_ARGS; HTTP_EMPTY_ARGS(getOptions, 0); @@ -158,6 +158,7 @@ HTTP_END_ARGS; HTTP_EMPTY_ARGS(getResponseMessage, 1); HTTP_EMPTY_ARGS(getRequestMessage, 1); HTTP_EMPTY_ARGS(getHistory, 1); +HTTP_EMPTY_ARGS(clearHistory, 0); HTTP_EMPTY_ARGS(send, 1); HTTP_BEGIN_ARGS(get, 0, 1) @@ -276,6 +277,7 @@ zend_function_entry http_request_object_fe[] = { HTTP_REQUEST_ME(getResponseMessage, ZEND_ACC_PUBLIC) HTTP_REQUEST_ME(getRequestMessage, ZEND_ACC_PUBLIC) HTTP_REQUEST_ME(getHistory, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(clearHistory, ZEND_ACC_PUBLIC) HTTP_REQUEST_ALIAS(get, http_get) HTTP_REQUEST_ALIAS(head, http_head) @@ -293,20 +295,30 @@ zend_function_entry http_request_object_fe[] = { }; static zend_object_handlers http_request_object_handlers; -void _http_request_object_init(INIT_FUNC_ARGS) +PHP_MINIT_FUNCTION(http_request_object) { HTTP_REGISTER_CLASS_EX(HttpRequest, http_request_object, NULL, 0); + http_request_object_handlers.clone_obj = _http_request_object_clone_obj; + return SUCCESS; } zend_object_value _http_request_object_new(zend_class_entry *ce TSRMLS_DC) +{ + return http_request_object_new_ex(ce, curl_easy_init(), NULL); +} + +zend_object_value _http_request_object_new_ex(zend_class_entry *ce, CURL *ch, http_request_object **ptr TSRMLS_DC) { zend_object_value ov; http_request_object *o; o = ecalloc(1, sizeof(http_request_object)); o->zo.ce = ce; - o->ch = curl_easy_init(); - o->pool = NULL; + o->ch = ch; + + if (ptr) { + *ptr = o; + } phpstr_init(&o->history); phpstr_init(&o->request); @@ -322,6 +334,24 @@ zend_object_value _http_request_object_new(zend_class_entry *ce TSRMLS_DC) return ov; } +zend_object_value _http_request_object_clone_obj(zval *this_ptr TSRMLS_DC) +{ + zend_object *old_zo; + zend_object_value new_ov; + http_request_object *new_obj; + getObject(http_request_object, old_obj); + + old_zo = zend_objects_get_address(this_ptr TSRMLS_CC); + new_ov = http_request_object_new_ex(old_zo->ce, curl_easy_duphandle(old_obj->ch), &new_obj); + + zend_objects_clone_members(&new_obj->zo, new_ov, old_zo, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC); + phpstr_append(&new_obj->history, old_obj->history.data, old_obj->history.used); + phpstr_append(&new_obj->request, old_obj->request.data, old_obj->request.used); + phpstr_append(&new_obj->response, old_obj->response.data, old_obj->response.used); + + return new_ov; +} + static inline void _http_request_object_declare_default_properties(TSRMLS_D) { zend_class_entry *ce = http_request_object_ce; @@ -342,8 +372,9 @@ static inline void _http_request_object_declare_default_properties(TSRMLS_D) DCL_PROP(PROTECTED, string, queryData, ""); DCL_PROP(PROTECTED, string, putFile, ""); - DCL_PROP(PUBLIC, bool, recordHistory, 1); + DCL_PROP(PUBLIC, bool, recordHistory, 0); +#ifndef WONKY /* * Request Method Constants */ @@ -388,6 +419,7 @@ static inline void _http_request_object_declare_default_properties(TSRMLS_D) DCL_CONST(long, "AUTH_NTLM", CURLAUTH_NTLM); DCL_CONST(long, "AUTH_ANY", CURLAUTH_ANY); # endif /* LIBCURL_VERSION_NUM */ +#endif /* WONKY */ } void _http_request_object_free(zend_object *object TSRMLS_DC) @@ -414,25 +446,28 @@ void _http_request_object_free(zend_object *object TSRMLS_DC) STATUS _http_request_object_requesthandler(http_request_object *obj, zval *this_ptr, http_request_body *body TSRMLS_DC) { - zval *meth, *URL; + zval *meth, *URL, *meth_p, *URL_p; char *request_uri; STATUS status = SUCCESS; if (!body) { return FAILURE; } - if ((!obj->ch) && (!(obj->ch = curl_easy_init()))) { - http_error(HE_WARNING, HTTP_E_REQUEST, "Could not initilaize curl"); - return FAILURE; - } + HTTP_CHECK_CURL_INIT(obj->ch, curl_easy_init(), return FAILURE); - URL = convert_to_type_ex(IS_STRING, GET_PROP(obj, url)); + URL = convert_to_type_ex(IS_STRING, GET_PROP(obj, url), &URL_p); // HTTP_URI_MAXLEN+1 long char * if (!(request_uri = http_absolute_uri_ex(Z_STRVAL_P(URL), Z_STRLEN_P(URL), NULL, 0, NULL, 0, 0))) { + if (URL_p) { + zval_ptr_dtor(&URL_p); + } return FAILURE; } + if (URL_p) { + zval_ptr_dtor(&URL_p); + } - meth = convert_to_type_ex(IS_LONG, GET_PROP(obj, method)); + meth = convert_to_type_ex(IS_LONG, GET_PROP(obj, method), &meth_p); switch (Z_LVAL_P(meth)) { case HTTP_GET: @@ -460,10 +495,10 @@ STATUS _http_request_object_requesthandler(http_request_object *obj, zval *this_ default: { /* check for raw post data */ - zval *raw_data = convert_to_type_ex(IS_STRING, GET_PROP(obj, rawPostData)); + zval *raw_data_p, *raw_data = convert_to_type_ex(IS_STRING, GET_PROP(obj, rawPostData), &raw_data_p); if (Z_STRLEN_P(raw_data)) { - zval *ctype = convert_to_type_ex(IS_STRING, GET_PROP(obj, contentType)); + zval *ctype_p, *ctype = convert_to_type_ex(IS_STRING, GET_PROP(obj, contentType), &ctype_p); if (Z_STRLEN_P(ctype)) { zval **headers, *opts = GET_PROP(obj, options); @@ -487,6 +522,10 @@ STATUS _http_request_object_requesthandler(http_request_object *obj, zval *this_ add_assoc_zval(opts, "headers", headers); } } + + if (ctype_p) { + zval_ptr_dtor(&ctype_p); + } body->type = HTTP_REQUEST_BODY_CSTRING; body->data = estrndup(Z_STRVAL_P(raw_data), Z_STRLEN_P(raw_data)); @@ -494,20 +533,32 @@ STATUS _http_request_object_requesthandler(http_request_object *obj, zval *this_ } else { status = http_request_body_fill(body, Z_ARRVAL_P(GET_PROP(obj, postFields)), Z_ARRVAL_P(GET_PROP(obj, postFiles))); } + + if (raw_data_p) { + zval_ptr_dtor(&raw_data_p); + } } break; } + + if (meth_p) { + zval_ptr_dtor(&meth_p); + } if (status == SUCCESS) { - zval *qdata = convert_to_type_ex(IS_STRING, GET_PROP(obj, queryData)); + zval *qdata_p, *qdata = convert_to_type_ex(IS_STRING, GET_PROP(obj, queryData), &qdata_p); - if (Z_STRLEN_P(qdata) && (strlen(request_uri) < HTTP_URI_MAXLEN)) { + if (Z_STRLEN_P(qdata)) { if (!strchr(request_uri, '?')) { - strcat(request_uri, "?"); + strlcat(request_uri, "?", HTTP_URI_MAXLEN); } else { - strcat(request_uri, "&"); + strlcat(request_uri, "&", HTTP_URI_MAXLEN); } - strncat(request_uri, Z_STRVAL_P(qdata), HTTP_URI_MAXLEN - strlen(request_uri)); + strlcat(request_uri, Z_STRVAL_P(qdata), HTTP_URI_MAXLEN); + } + + if (qdata_p) { + zval_ptr_dtor(&qdata_p); } status = http_request_init(obj->ch, Z_LVAL_P(meth), request_uri, body, Z_ARRVAL_P(GET_PROP(obj, options))); @@ -540,6 +591,9 @@ STATUS _http_request_object_responsehandler(http_request_object *obj, zval *this *resp = convert_to_type(IS_ARRAY, GET_PROP(obj, responseData)), *info = convert_to_type(IS_ARRAY, GET_PROP(obj, responseInfo)); + SEP_PROP(&resp); + SEP_PROP(&info); + if (zval_is_true(GET_PROP(obj, recordHistory))) { /* we need to act like a zipper, as we'll receive * the requests and the responses in separate chains @@ -576,9 +630,10 @@ STATUS _http_request_object_responsehandler(http_request_object *obj, zval *this add_assoc_zval(resp, "headers", headers); add_assoc_stringl(resp, "body", body, body_len, 0); + SET_PROP(obj, responseData, resp); MAKE_STD_ZVAL(message); - ZVAL_OBJVAL(message, http_message_object_from_msg(msg)); + ZVAL_OBJVAL(message, http_message_object_new_ex(http_message_object_ce, msg, NULL)); SET_PROP(obj, responseMessage, message); zval_ptr_dtor(&message); @@ -615,7 +670,7 @@ static inline void _http_request_object_set_options_subr(INTERNAL_FUNCTION_PARAM } } } else if (new_options && zend_hash_num_elements(Z_ARRVAL_P(new_options))) { - zval_add_ref(&new_options); + ZVAL_ADDREF(new_options); add_assoc_zval(opts, key, new_options); } @@ -629,10 +684,10 @@ static inline void _http_request_get_options_subr(INTERNAL_FUNCTION_PARAMETERS, NO_ARGS; IF_RETVAL_USED { - zval *opts, **options; + zval *opts_p, *opts, **options; getObject(http_request_object, obj); - opts = convert_to_type_ex(IS_ARRAY, GET_PROP(obj, options)); + opts = convert_to_type_ex(IS_ARRAY, GET_PROP(obj, options), &opts_p); array_init(return_value); @@ -640,26 +695,37 @@ static inline void _http_request_get_options_subr(INTERNAL_FUNCTION_PARAMETERS, convert_to_array(*options); array_copy(*options, return_value); } + + if (opts_p) { + zval_ptr_dtor(&opts_p); + } } } /* ### USERLAND ### */ -/* {{{ proto void HttpRequest::__construct([string url[, long request_method = HTTP_GET]]) +/* {{{ proto void HttpRequest::__construct([string url[, int request_method = HTTP_METH_GET[, array options]]]) * - * Instantiate a new HttpRequest object which can be used to issue HEAD, GET - * and POST (including posting files) HTTP requests. + * Instantiate a new HttpRequest object. + * + * Accepts a string as optional parameter containing the target request url. + * Additianally accepts an optional int parameter specifying the request method + * to use and an associative array as optional third parameter which will be + * passed to HttpRequest::setOptions(). + * + * Throws HttpException. */ PHP_METHOD(HttpRequest, __construct) { char *URL = NULL; int URL_len; long meth = -1; + zval *options = NULL; getObject(http_request_object, obj); SET_EH_THROW_HTTP(); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sl", &URL, &URL_len, &meth)) { + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sla", &URL, &URL_len, &meth, &options)) { INIT_PARR(obj, options); INIT_PARR(obj, responseInfo); INIT_PARR(obj, responseData); @@ -672,6 +738,9 @@ PHP_METHOD(HttpRequest, __construct) if (meth > -1) { UPD_PROP(obj, long, method, meth); } + if (options) { + zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "setoptions", NULL, options); + } } SET_EH_NORMAL(); } @@ -698,32 +767,41 @@ PHP_METHOD(HttpRequest, __destruct) /* {{{ proto bool HttpRequest::setOptions([array options]) * * Set the request options to use. See http_get() for a full list of available options. + * + * Accepts an array as optional parameters, wich values will overwrite the + * currently set request options. If the parameter is empty or mitted, + * the optoions of the HttpRequest object will be reset. + * + * Returns TRUE on success, or FALSE on failure. */ PHP_METHOD(HttpRequest, setOptions) { char *key = NULL; ulong idx = 0; + HashPosition pos; zval *opts = NULL, *old_opts, **opt; getObject(http_request_object, obj); - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a/!", &opts)) { + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &opts)) { RETURN_FALSE; } old_opts = convert_to_type(IS_ARRAY, GET_PROP(obj, options)); - + SEP_PROP(&old_opts); + if (!opts || !zend_hash_num_elements(Z_ARRVAL_P(opts))) { zend_hash_clean(Z_ARRVAL_P(old_opts)); + SET_PROP(obj, options, old_opts); RETURN_TRUE; } - + /* some options need extra attention -- thus cannot use array_merge() directly */ - FOREACH_KEYVAL(opts, key, idx, opt) { + FOREACH_KEYVAL(pos, opts, key, idx, opt) { if (key) { if (!strcmp(key, "headers")) { zval **headers; if (SUCCESS == zend_hash_find(Z_ARRVAL_P(old_opts), "headers", sizeof("headers"), (void **) &headers)) { - convert_to_array(*opt); + convert_to_array_ex(opt); convert_to_array(*headers); array_merge(*opt, *headers); continue; @@ -731,7 +809,7 @@ PHP_METHOD(HttpRequest, setOptions) } else if (!strcmp(key, "cookies")) { zval **cookies; if (SUCCESS == zend_hash_find(Z_ARRVAL_P(old_opts), "cookies", sizeof("cookies"), (void **) &cookies)) { - convert_to_array(*opt); + convert_to_array_ex(opt); convert_to_array(*cookies); array_merge(*opt, *cookies); continue; @@ -739,7 +817,7 @@ PHP_METHOD(HttpRequest, setOptions) } else if (!strcmp(key, "ssl")) { zval **ssl; if (SUCCESS == zend_hash_find(Z_ARRVAL_P(old_opts), "ssl", sizeof("ssl"), (void **) &ssl)) { - convert_to_array(*opt); + convert_to_array_ex(opt); convert_to_array(*ssl); array_merge(*opt, *ssl); continue; @@ -758,13 +836,14 @@ PHP_METHOD(HttpRequest, setOptions) continue; } - zval_add_ref(opt); + ZVAL_ADDREF(*opt); add_assoc_zval(old_opts, key, *opt); /* reset */ key = NULL; } } + SET_PROP(obj, options, old_opts); RETURN_TRUE; } @@ -773,18 +852,24 @@ PHP_METHOD(HttpRequest, setOptions) /* {{{ proto array HttpRequest::getOptions() * * Get currently set options. + * + * Returns an associative array containing currently set options. */ PHP_METHOD(HttpRequest, getOptions) { NO_ARGS; IF_RETVAL_USED { - zval *opts; + zval *opts_p, *opts; getObject(http_request_object, obj); - opts = convert_to_type_ex(IS_ARRAY, GET_PROP(obj, options)); + opts = convert_to_type_ex(IS_ARRAY, GET_PROP(obj, options), &opts_p); array_init(return_value); array_copy(opts, return_value); + + if (opts_p) { + zval_ptr_dtor(&opts_p); + } } } /* }}} */ @@ -792,6 +877,11 @@ PHP_METHOD(HttpRequest, getOptions) /* {{{ proto bool HttpRequest::setSslOptions([array options]) * * Set SSL options. + * + * Accepts an associative array as parameter containing any SSL specific options. + * If the parameter is empty or omitted, the SSL options will be reset. + * + * Returns TRUE on success, or FALSE on failure. */ PHP_METHOD(HttpRequest, setSslOptions) { @@ -802,6 +892,10 @@ PHP_METHOD(HttpRequest, setSslOptions) /* {{{ proto bool HttpRequest::addSslOptions(array options) * * Set additional SSL options. + * + * Expects an associative array as parameter containing additional SSL specific options. + * + * Returns TRUE on success, or FALSE on failure. */ PHP_METHOD(HttpRequest, addSslOptions) { @@ -812,6 +906,8 @@ PHP_METHOD(HttpRequest, addSslOptions) /* {{{ proto array HttpRequest::getSslOtpions() * * Get previously set SSL options. + * + * Returns an associative array containing any previously set SSL options. */ PHP_METHOD(HttpRequest, getSslOptions) { @@ -822,6 +918,11 @@ PHP_METHOD(HttpRequest, getSslOptions) /* {{{ proto bool HttpRequest::addHeaders(array headers) * * Add request header name/value pairs. + * + * Expects an ssociative array as parameter containing additional header + * name/value pairs. + * + * Returns TRUE on success, or FALSE on failure. */ PHP_METHOD(HttpRequest, addHeaders) { @@ -831,6 +932,11 @@ PHP_METHOD(HttpRequest, addHeaders) /* {{{ proto bool HttpRequest::setHeaders([array headers]) * * Set request header name/value pairs. + * + * Accepts an associative array as parameter containing header name/value pairs. + * If the parameter is empty or omitted, all previously set headers will be unset. + * + * Returns TRUE on success, or FALSE on failure. */ PHP_METHOD(HttpRequest, setHeaders) { @@ -841,6 +947,8 @@ PHP_METHOD(HttpRequest, setHeaders) /* {{{ proto array HttpRequest::getHeaders() * * Get previously set request headers. + * + * Returns an associative array containing all currently set headers. */ PHP_METHOD(HttpRequest, getHeaders) { @@ -851,6 +959,11 @@ PHP_METHOD(HttpRequest, getHeaders) /* {{{ proto bool HttpRequest::setCookies([array cookies]) * * Set cookies. + * + * Accepts an associative array as parameter containing cookie name/value pairs. + * If the parameter is empty or omitted, all previously set cookies will be unset. + * + * Returns TRUE on success, or FALSE on failure. */ PHP_METHOD(HttpRequest, setCookies) { @@ -861,6 +974,11 @@ PHP_METHOD(HttpRequest, setCookies) /* {{{ proto bool HttpRequest::addCookies(array cookies) * * Add cookies. + * + * Expects an associative array as parameter containing any cookie name/value + * pairs to add. + * + * Returns TRUE on success, or FALSE on failure. */ PHP_METHOD(HttpRequest, addCookies) { @@ -871,6 +989,8 @@ PHP_METHOD(HttpRequest, addCookies) /* {{{ proto array HttpRequest::getCookies() * * Get previously set cookies. + * + * Returns an associative array containing any previously set cookies. */ PHP_METHOD(HttpRequest, getCookies) { @@ -881,6 +1001,10 @@ PHP_METHOD(HttpRequest, getCookies) /* {{{ proto bool HttpRequest::setUrl(string url) * * Set the request URL. + * + * Expects a string as parameter specifying the request url. + * + * Returns TRUE on success, or FALSE on failure. */ PHP_METHOD(HttpRequest, setUrl) { @@ -900,6 +1024,8 @@ PHP_METHOD(HttpRequest, setUrl) /* {{{ proto string HttpRequest::getUrl() * * Get the previously set request URL. + * + * Returns the currently set request url as string. */ PHP_METHOD(HttpRequest, getUrl) { @@ -914,10 +1040,14 @@ PHP_METHOD(HttpRequest, getUrl) } /* }}} */ -/* {{{ proto bool HttpRequest::setMethod(long request_method) +/* {{{ proto bool HttpRequest::setMethod(int request_method) * - * Set the request methods; one of the HTTP_HEAD, HTTP_GET or - * HTTP_POST constants. + * Set the request method. + * + * Expects an int as parameter specifying the request method to use. + * In PHP 5.1+ HttpRequest::METH, otherwise the HTTP_METH constants can be used. + * + * Returns TRUE on success, or FALSE on failure. */ PHP_METHOD(HttpRequest, setMethod) { @@ -933,9 +1063,11 @@ PHP_METHOD(HttpRequest, setMethod) } /* }}} */ -/* {{{ proto long HttpRequest::getMethod() +/* {{{ proto int HttpRequest::getMethod() * * Get the previously set request method. + * + * Returns the currently set request method. */ PHP_METHOD(HttpRequest, getMethod) { @@ -953,7 +1085,12 @@ PHP_METHOD(HttpRequest, getMethod) /* {{{ proto bool HttpRequest::setContentType(string content_type) * * Set the content type the post request should have. - * Use this only if you know what you're doing. + * + * Expects a string as parameters containing the content type of the request + * (primary/secondary). + * + * Returns TRUE on success, or FALSE if the content type does not seem to + * contain a primary and a secondary part. */ PHP_METHOD(HttpRequest, setContentType) { @@ -965,11 +1102,7 @@ PHP_METHOD(HttpRequest, setContentType) RETURN_FALSE; } - if (!strchr(ctype, '/')) { - http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Content-Type '%s' doesn't seem to contain a primary and a secondary part", ctype); - RETURN_FALSE; - } - + HTTP_CHECK_CONTENT_TYPE(ctype, RETURN_FALSE); UPD_STRL(obj, contentType, ctype, ct_len); RETURN_TRUE; } @@ -978,6 +1111,8 @@ PHP_METHOD(HttpRequest, setContentType) /* {{{ proto string HttpRequest::getContentType() * * Get the previously content type. + * + * Returns the previously set content type as string. */ PHP_METHOD(HttpRequest, getContentType) { @@ -994,9 +1129,14 @@ PHP_METHOD(HttpRequest, getContentType) /* {{{ proto bool HttpRequest::setQueryData([mixed query_data]) * - * Set the URL query parameters to use. - * Overwrites previously set query parameters. + * Set the URL query parameters to use, overwriting previously set query parameters. * Affects any request types. + * + * Accepts a string or associative array parameter containing the pre-encoded + * query string or to be encoded query fields. If the parameter is empty or + * omitted, the query data will be unset. + * + * Returns TRUE on success, or FALSE on failure. */ PHP_METHOD(HttpRequest, setQueryData) { @@ -1029,6 +1169,8 @@ PHP_METHOD(HttpRequest, setQueryData) /* {{{ proto string HttpRequest::getQueryData() * * Get the current query data in form of an urlencoded query string. + * + * Returns a string containing the urlencoded query. */ PHP_METHOD(HttpRequest, getQueryData) { @@ -1036,21 +1178,29 @@ PHP_METHOD(HttpRequest, getQueryData) IF_RETVAL_USED { getObject(http_request_object, obj); - zval *qdata = convert_to_type_ex(IS_STRING, GET_PROP(obj, queryData)); + zval *qdata_p, *qdata = convert_to_type_ex(IS_STRING, GET_PROP(obj, queryData), &qdata_p); RETURN_ZVAL(qdata, 1, 0); + + if (qdata_p) { + zval_ptr_dtor(&qdata_p); + } } } /* }}} */ /* {{{ proto bool HttpRequest::addQueryData(array query_params) * - * Add parameters to the query parameter list. + * Add parameters to the query parameter list, leaving previously set unchanged. * Affects any request type. + * + * Expects an associative array as parameter containing the query fields to add. + * + * Returns TRUE on success, or FALSE on failure. */ PHP_METHOD(HttpRequest, addQueryData) { - zval *qdata, *old_qdata; + zval *qdata, *old_qdata, *old_qdata_p; char *query_data = NULL; size_t query_data_len = 0; getObject(http_request_object, obj); @@ -1059,23 +1209,35 @@ PHP_METHOD(HttpRequest, addQueryData) RETURN_FALSE; } - old_qdata = convert_to_type_ex(IS_STRING, GET_PROP(obj, queryData)); + old_qdata = convert_to_type_ex(IS_STRING, GET_PROP(obj, queryData), &old_qdata_p); if (SUCCESS != http_urlencode_hash_ex(HASH_OF(qdata), 1, Z_STRVAL_P(old_qdata), Z_STRLEN_P(old_qdata), &query_data, &query_data_len)) { + if (old_qdata_p) { + zval_ptr_dtor(&old_qdata_p); + } RETURN_FALSE; } UPD_STRL(obj, queryData, query_data, query_data_len); efree(query_data); + if (old_qdata_p) { + zval_ptr_dtor(&old_qdata_p); + } + RETURN_TRUE; } /* }}} */ /* {{{ proto bool HttpRequest::addPostFields(array post_data) * - * Adds POST data entries. - * Affects only POST requests. + * Adds POST data entries, leaving previously set unchanged, unless a + * post entry with the same name already exists. + * Affects only POST and custom requests. + * + * Expects an associative array as parameter containing the post fields. + * + * Returns TRUE on success, or FALSE on failure. */ PHP_METHOD(HttpRequest, addPostFields) { @@ -1095,9 +1257,13 @@ PHP_METHOD(HttpRequest, addPostFields) /* {{{ proto bool HttpRequest::setPostFields([array post_data]) * - * Set the POST data entries. - * Overwrites previously set POST data. - * Affects only POST requests. + * Set the POST data entries, overwriting previously set POST data. + * Affects only POST and custom requests. + * + * Accepts an associative array as parameter containing the post fields. + * If the parameter is empty or omitted, the post data will be unset. + * + * Returns TRUE on success, or FALSE on failure. */ PHP_METHOD(HttpRequest, setPostFields) { @@ -1109,11 +1275,13 @@ PHP_METHOD(HttpRequest, setPostFields) } post = convert_to_type(IS_ARRAY, GET_PROP(obj, postFields)); - zend_hash_clean(Z_ARRVAL_P(post)); + SEP_PROP(&post); + zend_hash_clean(Z_ARRVAL_P(post)); if (post_data && zend_hash_num_elements(Z_ARRVAL_P(post_data))) { array_copy(post_data, post); } + SET_PROP(obj, postFields, post); RETURN_TRUE; } @@ -1122,6 +1290,8 @@ PHP_METHOD(HttpRequest, setPostFields) /* {{{ proto array HttpRequest::getPostFields() * * Get previously set POST data. + * + * Returns the currently set post fields as associative array. */ PHP_METHOD(HttpRequest, getPostFields) { @@ -1129,18 +1299,29 @@ PHP_METHOD(HttpRequest, getPostFields) IF_RETVAL_USED { getObject(http_request_object, obj); - zval *post_data = convert_to_type_ex(IS_ARRAY, GET_PROP(obj, postFields)); + zval *post_data_p, *post_data = convert_to_type_ex(IS_ARRAY, GET_PROP(obj, postFields), &post_data_p); array_init(return_value); array_copy(post_data, return_value); + + if (post_data_p) { + zval_ptr_dtor(&post_data_p); + } } } /* }}} */ /* {{{ proto bool HttpRequest::setRawPostData([string raw_post_data]) * - * Set raw post data to send. Don't forget to specify a content type. - * Affects only POST requests. + * Set raw post data to send, overwriting previously set raw post data. Don't + * forget to specify a content type. Affects only POST and custom requests. + * Only either post fields or raw post data can be used for each request. + * Raw post data has higher precedence and will be used even if post fields + * are set. + * + * Accepts a string as parameter containing the *raw* post data. + * + * Returns TRUE on success, or FALSE on failure. */ PHP_METHOD(HttpRequest, setRawPostData) { @@ -1163,8 +1344,12 @@ PHP_METHOD(HttpRequest, setRawPostData) /* {{{ proto bool HttpRequest::addRawPostData(string raw_post_data) * - * Add raw post data. - * Affects only POST requests. + * Add raw post data, leaving previously set raw post data unchanged. + * Affects only POST and custom requests. + * + * Expects a string as parameter containing the raw post data to concatenate. + * + * Returns TRUE on success, or FALSE on failure. */ PHP_METHOD(HttpRequest, addRawPostData) { @@ -1177,7 +1362,7 @@ PHP_METHOD(HttpRequest, addRawPostData) } if (data_len) { - zval *zdata = convert_to_type_ex(IS_STRING, GET_PROP(obj, rawPostData)); + zval *zdata_p, *zdata = convert_to_type_ex(IS_STRING, GET_PROP(obj, rawPostData), &zdata_p); new_data = emalloc(Z_STRLEN_P(zdata) + data_len + 1); new_data[Z_STRLEN_P(zdata) + data_len] = '\0'; @@ -1188,6 +1373,10 @@ PHP_METHOD(HttpRequest, addRawPostData) memcpy(new_data + Z_STRLEN_P(zdata), raw_data, data_len); UPD_STRL(obj, rawPostData, new_data, Z_STRLEN_P(zdata) + data_len); + + if (zdata_p) { + zval_ptr_dtor(&zdata_p); + } } RETURN_TRUE; @@ -1197,6 +1386,8 @@ PHP_METHOD(HttpRequest, addRawPostData) /* {{{ proto string HttpRequest::getRawPostData() * * Get previously set raw post data. + * + * Returns a string containing the currently set raw post data. */ PHP_METHOD(HttpRequest, getRawPostData) { @@ -1204,17 +1395,29 @@ PHP_METHOD(HttpRequest, getRawPostData) IF_RETVAL_USED { getObject(http_request_object, obj); - zval *raw_data = convert_to_type_ex(IS_STRING, GET_PROP(obj, rawPostData)); + zval *raw_data_p, *raw_data = convert_to_type_ex(IS_STRING, GET_PROP(obj, rawPostData), &raw_data_p); - RETURN_ZVAL(raw_data, 1, 0); + RETVAL_ZVAL(raw_data, 1, 0); + + if (raw_data_p) { + zval_ptr_dtor(&raw_data_p); + } } } /* }}} */ /* {{{ proto bool HttpRequest::addPostFile(string name, string file[, string content_type = "application/x-octetstream"]) * - * Add a file to the POST request. - * Affects only POST requests. + * Add a file to the POST request, leaving prefiously set files unchanged. + * Affects only POST and custom requests. Cannot be used with raw post data. + * + * Expects a string parameter containing the form element name, and a string + * paremeter containing the path to the file which should be uploaded. + * Additionally accepts an optional string parameter which chould contain + * the content type of the file. + * + * Returns TRUE on success, or FALSE if the content type seems not to contain a + * primary and a secondary content type part. */ PHP_METHOD(HttpRequest, addPostFile) { @@ -1228,10 +1431,7 @@ PHP_METHOD(HttpRequest, addPostFile) } if (type_len) { - if (!strchr(type, '/')) { - http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Content-Type '%s' doesn't seem to contain a primary and a secondary part", type); - RETURN_FALSE; - } + HTTP_CHECK_CONTENT_TYPE(type, RETURN_FALSE); } else { type = "application/x-octetstream"; type_len = sizeof("application/x-octetstream") - 1; @@ -1253,9 +1453,14 @@ PHP_METHOD(HttpRequest, addPostFile) /* {{{ proto bool HttpRequest::setPostFiles([array post_files]) * - * Set files to post. - * Overwrites previously set post files. - * Affects only POST requests. + * Set files to post, overwriting previously set post files. + * Affects only POST and requests. Cannot be used with raw post data. + * + * Accepts an array containing the files to post. Each entry should be an + * associative array with "name", "file" and "type" keys. If the parameter + * is empty or omitted the post files will be unset. + * + * Returns TRUE on success, or FALSE on failure. */ PHP_METHOD(HttpRequest, setPostFiles) { @@ -1267,11 +1472,13 @@ PHP_METHOD(HttpRequest, setPostFiles) } pFiles = convert_to_type(IS_ARRAY, GET_PROP(obj, postFiles)); - zend_hash_clean(Z_ARRVAL_P(pFiles)); + SEP_PROP(&pFiles); + zend_hash_clean(Z_ARRVAL_P(pFiles)); if (files && zend_hash_num_elements(Z_ARRVAL_P(files))) { array_copy(files, pFiles); } + SET_PROP(obj, postFiles, pFiles); RETURN_TRUE; } @@ -1280,6 +1487,8 @@ PHP_METHOD(HttpRequest, setPostFiles) /* {{{ proto array HttpRequest::getPostFiles() * * Get all previously added POST files. + * + * Returns an array containing currently set post files. */ PHP_METHOD(HttpRequest, getPostFiles) { @@ -1287,18 +1496,26 @@ PHP_METHOD(HttpRequest, getPostFiles) IF_RETVAL_USED { getObject(http_request_object, obj); - zval *files = convert_to_type_ex(IS_ARRAY, GET_PROP(obj, postFiles)); + zval *files_p, *files = convert_to_type_ex(IS_ARRAY, GET_PROP(obj, postFiles), &files_p); array_init(return_value); array_copy(files, return_value); + + if (files_p) { + zval_ptr_dtor(&files_p); + } } } /* }}} */ /* {{{ proto bool HttpRequest::setPutFile([string file]) * - * Set file to put. - * Affects only PUT requests. + * Set file to put. Affects only PUT requests. + * + * Accepts a string as parameter referencing the path to file. + * If the parameter is empty or omitted the put file will be unset. + * + * Returns TRUE on success, or FALSE on failure. */ PHP_METHOD(HttpRequest, setPutFile) { @@ -1318,6 +1535,8 @@ PHP_METHOD(HttpRequest, setPutFile) /* {{{ proto string HttpRequest::getPutFile() * * Get previously set put file. + * + * Returns a string containing the path to the currently set put file. */ PHP_METHOD(HttpRequest, getPutFile) { @@ -1325,9 +1544,13 @@ PHP_METHOD(HttpRequest, getPutFile) IF_RETVAL_USED { getObject(http_request_object, obj); - zval *putfile = convert_to_type_ex(IS_STRING, GET_PROP(obj, putFile)); + zval *putfile_p, *putfile = convert_to_type_ex(IS_STRING, GET_PROP(obj, putFile), &putfile_p); - RETURN_ZVAL(putfile, 1, 0); + RETVAL_ZVAL(putfile, 1, 0); + + if (putfile_p) { + zval_ptr_dtor(&putfile_p); + } } } /* }}} */ @@ -1335,6 +1558,13 @@ PHP_METHOD(HttpRequest, getPutFile) /* {{{ proto array HttpRequest::getResponseData() * * Get all response data after the request has been sent. + * + * Returns an associative array with the key "headers" containing an associative + * array holding all response headers, as well as the ley "body" containing a + * string with the response body. + * + * If redirects were allowed and several responses were received, the data + * references the last received response. */ PHP_METHOD(HttpRequest, getResponseData) { @@ -1342,10 +1572,14 @@ PHP_METHOD(HttpRequest, getResponseData) IF_RETVAL_USED { getObject(http_request_object, obj); - zval *data = convert_to_type_ex(IS_ARRAY, GET_PROP(obj, responseData)); + zval *data_p, *data = convert_to_type_ex(IS_ARRAY, GET_PROP(obj, responseData), &data_p); array_init(return_value); array_copy(data, return_value); + + if (data_p) { + zval_ptr_dtor(&data_p); + } } } /* }}} */ @@ -1353,11 +1587,20 @@ PHP_METHOD(HttpRequest, getResponseData) /* {{{ proto mixed HttpRequest::getResponseHeader([string name]) * * Get response header(s) after the request has been sent. + * + * Accepts an string as optional parameter specifying a certain header to read. + * If the parameter is empty or omitted all response headers will be returned. + * + * Returns either a string with the value of the header matching name if requested, + * FALSE on failure, or an associative array containing all reponse headers. + * + * If redirects were allowed and several responses were received, the data + * references the last received response. */ PHP_METHOD(HttpRequest, getResponseHeader) { IF_RETVAL_USED { - zval *data, **headers, **header; + zval *data_p, *data, **headers, **header; char *header_name = NULL; int header_len = 0; getObject(http_request_object, obj); @@ -1366,18 +1609,23 @@ PHP_METHOD(HttpRequest, getResponseHeader) RETURN_FALSE; } - data = convert_to_type_ex(IS_ARRAY, GET_PROP(obj, responseData)); - if (SUCCESS != zend_hash_find(Z_ARRVAL_P(data), "headers", sizeof("headers"), (void **) &headers)) { - RETURN_FALSE; - } - convert_to_array_ex(headers); - if (!header_len || !header_name) { - array_init(return_value); - array_copy(*headers, return_value); - } else if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(headers), pretty_key(header_name, header_len, 1, 1), header_len + 1, (void **) &header)) { - RETURN_ZVAL(*header, 1, 0); + data = convert_to_type_ex(IS_ARRAY, GET_PROP(obj, responseData), &data_p); + if (SUCCESS == zend_hash_find(Z_ARRVAL_P(data), "headers", sizeof("headers"), (void **) &headers)) { + convert_to_array(*headers); + if (!header_len || !header_name) { + array_init(return_value); + array_copy(*headers, return_value); + } else if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(headers), pretty_key(header_name, header_len, 1, 1), header_len + 1, (void **) &header)) { + RETVAL_ZVAL(*header, 1, 0); + } else { + RETVAL_FALSE; + } } else { - RETURN_FALSE; + RETVAL_FALSE; + } + + if (data_p) { + zval_ptr_dtor(&data_p); } } } @@ -1386,6 +1634,17 @@ PHP_METHOD(HttpRequest, getResponseHeader) /* {{{ proto array HttpRequest::getResponseCookie([string name]) * * Get response cookie(s) after the request has been sent. + * + * Accepts a string as optional parameter specifying the name of the cookie to read. + * If the parameter is empty or omitted, an associative array with all received + * cookies will be returned. + * + * Returns either an associative array with the cookie's name, value and any + * additional params of the cookie matching name if requested, FALSE on failure, + * or an array containing all received cookies as arrays. + * + * If redirects were allowed and several responses were received, the data + * references the last received response. */ PHP_METHOD(HttpRequest, getResponseCookie) { @@ -1401,20 +1660,22 @@ PHP_METHOD(HttpRequest, getResponseCookie) array_init(return_value); - data = convert_to_type_ex(IS_ARRAY, GET_PROP(obj, responseData)); + data = convert_to_type(IS_ARRAY, GET_PROP(obj, responseData)); if (SUCCESS == zend_hash_find(Z_ARRVAL_P(data), "headers", sizeof("headers"), (void **) &headers)) { ulong idx = 0; char *key = NULL; zval **header = NULL; + HashPosition pos1; - convert_to_array_ex(headers); - FOREACH_HASH_KEYVAL(Z_ARRVAL_PP(headers), key, idx, header) { + convert_to_array(*headers); + FOREACH_HASH_KEYVAL(pos1, Z_ARRVAL_PP(headers), key, idx, header) { if (key && !strcasecmp(key, "Set-Cookie")) { /* several cookies? */ if (Z_TYPE_PP(header) == IS_ARRAY) { zval **cookie; + HashPosition pos2; - FOREACH_HASH_VAL(Z_ARRVAL_PP(header), cookie) { + FOREACH_HASH_VAL(pos2, Z_ARRVAL_PP(header), cookie) { zval *cookie_hash; MAKE_STD_ZVAL(cookie_hash); array_init(cookie_hash); @@ -1478,6 +1739,11 @@ PHP_METHOD(HttpRequest, getResponseCookie) /* {{{ proto string HttpRequest::getResponseBody() * * Get the response body after the request has been sent. + * + * Returns a string containing the response body. + * + * If redirects were allowed and several responses were received, the data + * references the last received response. */ PHP_METHOD(HttpRequest, getResponseBody) { @@ -1486,10 +1752,9 @@ PHP_METHOD(HttpRequest, getResponseBody) IF_RETVAL_USED { zval **body; getObject(http_request_object, obj); - zval *data = convert_to_type_ex(IS_ARRAY, GET_PROP(obj, responseData)); + zval *data = convert_to_type(IS_ARRAY, GET_PROP(obj, responseData)); if (SUCCESS == zend_hash_find(Z_ARRVAL_P(data), "body", sizeof("body"), (void **) &body)) { - convert_to_string_ex(body); RETURN_ZVAL(*body, 1, 0); } else { RETURN_FALSE; @@ -1501,6 +1766,11 @@ PHP_METHOD(HttpRequest, getResponseBody) /* {{{ proto int HttpRequest::getResponseCode() * * Get the response code after the request has been sent. + * + * Returns an int representing the response code. + * + * If redirects were allowed and several responses were received, the data + * references the last received response. */ PHP_METHOD(HttpRequest, getResponseCode) { @@ -1508,17 +1778,32 @@ PHP_METHOD(HttpRequest, getResponseCode) IF_RETVAL_USED { getObject(http_request_object, obj); - zval *code = convert_to_type_ex(IS_LONG, GET_PROP(obj, responseCode)); + zval *code_p, *code = convert_to_type_ex(IS_LONG, GET_PROP(obj, responseCode), &code_p); - RETURN_ZVAL(code, 1, 0); + RETVAL_ZVAL(code, 1, 0); + + if (code_p) { + zval_ptr_dtor(&code_p); + } } } /* }}} */ -/* {{{ proto array HttpRequest::getResponseInfo([string name]) +/* {{{ proto mixed HttpRequest::getResponseInfo([string name]) * * Get response info after the request has been sent. * See http_get() for a full list of returned info. + * + * Accepts a string as optional parameter specifying the info to read. + * If the parameter is empty or omitted, an associative array containing + * all available info will be returned. + * + * Returns either a scalar containing the value of the info matching name if + * requested, FALSE on failure, or an associative array containing all + * available info. + * + * If redirects were allowed and several responses were received, the data + * references the last received response. */ PHP_METHOD(HttpRequest, getResponseInfo) { @@ -1532,7 +1817,7 @@ PHP_METHOD(HttpRequest, getResponseInfo) RETURN_FALSE; } - info = convert_to_type_ex(IS_ARRAY, GET_PROP(obj, responseInfo)); + info = convert_to_type(IS_ARRAY, GET_PROP(obj, responseInfo)); if (info_len && info_name) { if (SUCCESS == zend_hash_find(Z_ARRVAL_P(info), pretty_key(info_name, info_len, 0, 0), info_len + 1, (void **) &infop)) { @@ -1551,7 +1836,16 @@ PHP_METHOD(HttpRequest, getResponseInfo) /* {{{ proto HttpMessage HttpRequest::getResponseMessage() * - * Get the full response as HttpMessage object. + * Get the full response as HttpMessage object after the request has been sent. + * + * Returns an HttpMessage object of the response. + * + * If redirects were allowed and several responses were received, the data + * references the last received response. Use HttpMessage::getParentMessage() + * to access the data of previously received responses whithin this request + * cycle. + * + * Throws HttpException. */ PHP_METHOD(HttpRequest, getResponseMessage) { @@ -1576,6 +1870,20 @@ PHP_METHOD(HttpRequest, getResponseMessage) /* {{{ proto HttpMessage HttpRequest::getRequestMessage() * * Get sent HTTP message. + * + * Returns an HttpMessage object representing the sent request. + * + * If redirects were allowed and several responses were received, the data + * references the last received response. Use HttpMessage::getParentMessage() + * to access the data of previously sent requests whithin this request + * cycle. + * + * Note that the internal request message is immutable, that means that the + * request message received through HttpRequest::getRequestMessage() will + * always look the same for the same request, regardless of any changes you + * may have made to the returned object. + * + * Throws HttpMalformedHeadersException, HttpEncodingException. */ PHP_METHOD(HttpRequest, getRequestMessage) { @@ -1587,13 +1895,32 @@ PHP_METHOD(HttpRequest, getRequestMessage) SET_EH_THROW_HTTP(); if (msg = http_message_parse(PHPSTR_VAL(&obj->request), PHPSTR_LEN(&obj->request))) { - RETVAL_OBJVAL(http_message_object_from_msg(msg)); + ZVAL_OBJVAL(return_value, http_message_object_new_ex(http_message_object_ce, msg, NULL)); } SET_EH_NORMAL(); } } /* }}} */ +/* {{{ proto HttpMessage HttpRequest::getHistory() + * + * Get all sent requests and received responses as an HttpMessage object. + * + * If you don't want to record history at all, set the instance variable + * HttpRequest::$recoedHistory to FALSE. + * + * Returns an HttpMessage object representing the complete request/response + * history. + * + * The object references the last received response, use HttpMessage::getParentMessage() + * to access the data of previously sent requests and received responses. + * + * Note that the internal history is immutable, that means that any changes + * you make the the message list won't affect a history message list newly + * created by another call to HttpRequest::getHistory(). + * + * Throws HttpMalformedHeaderException, HttpEncodingException. + */ PHP_METHOD(HttpRequest, getHistory) { NO_ARGS; @@ -1604,15 +1931,38 @@ PHP_METHOD(HttpRequest, getHistory) SET_EH_THROW_HTTP(); if (msg = http_message_parse(PHPSTR_VAL(&obj->history), PHPSTR_LEN(&obj->history))) { - RETVAL_OBJVAL(http_message_object_from_msg(msg)); + ZVAL_OBJVAL(return_value, http_message_object_new_ex(http_message_object_ce, msg, NULL)); } SET_EH_NORMAL(); } } +/* }}} */ + +/* {{{ proto void HttpRequest::clearHistory() + * + * Clear the history. + */ +PHP_METHOD(HttpRequest, clearHistory) +{ + NO_ARGS { + getObject(http_request_object, obj); + phpstr_dtor(&obj->history); + } +} +/* }}} */ /* {{{ proto HttpMessage HttpRequest::send() * * Send the HTTP request. + * + * Returns the received response as HttpMessage object. + * + * NOTE: While an exception may be thrown, the transfer could have succeeded + * at least partially, so you might want to check the return values of various + * HttpRequest::getResponse*() methods. + * + * Throws HttpRuntimeException, HttpRequestException, + * HttpMalformedHeaderException, HttpEncodingException. * * GET example: *