Internals:
- the request bodies created in http_request_pool_attach() are not
destroyed in http_request_pool_detach(); may be a memory problem
- in long running scripts
\ No newline at end of file
+ in long running scripts
+ - protected class members are not type safe
--- /dev/null
+<?php
+// send 5000 bytes every 0.2 seconds, i.e. max ~25kByte/s
+HttpResponse::setThrottleDelay(0.2);
+HttpResponse::setBufferSize(5000);
+HttpResponse::setCache(true);
+HttpResponse::setContentType('application/x-zip');
+HttpResponse::setFile('../archive.zip');
+HttpResponse::send();
+?>
--- /dev/null
+<?php
+HttpResponse::setCacheControl('public');
+HttpResponse::setCache(true);
+HttpResponse::capture();
+
+print "This will be cached until content changes!\n";
+print "Note that this approach will only save the clients download time.\n";
+?>
--- /dev/null
+<?php
+$r = new HttpRequest('http://www.google.com');
+
+// store Googles cookies in a dedicated file
+$r->setOptions(
+ array( 'cookiestore' => '../cookies/google.txt',
+ )
+);
+
+$r->setQueryData(
+ array( 'q' => '+"pecl_http" -msg -cvs -list',
+ 'hl' => 'de'
+ )
+);
+
+// HttpRequest::send() returns an HttpMessage object
+// of type HttpMessage::RESPONSE or throws an exception
+try {
+ print $r->send()->getBody();
+} catch (HttpException $e) {
+ print $e;
+}
+?>
--- /dev/null
+<?php
+class XmlRpcClient
+{
+ public $namespace;
+ protected $request;
+
+ public function __construct($url, $namespace = '')
+ {
+ $this->namespace = $namespace;
+ $this->request = new HttpRequest($url, HTTP_POST);
+ $this->request->setContentType('text/xml');
+ }
+
+ public function setOptions($options = array())
+ {
+ return $this->request->setOptions($options);
+ }
+
+ public function addOptions($options)
+ {
+ return $this->request->addOptions($options);
+ }
+
+ public function __call($method, $params)
+ {
+ if ($this->namespace) {
+ $method = $this->namespace .'.'. $method;
+ }
+ $this->request->setPostData(xmlrpc_encode_request($method, $params));
+ $response = $this->request->send();
+ if ($response->getResponseCode() != 200) {
+ throw new Exception($response->getBody(), $response->getResponseCode());
+ }
+ return xmlrpc_decode($response->getBody(), 'utf-8');
+ }
+
+ public function getHistory()
+ {
+ return $this->request->getHistory();
+ }
+}
+
+?>
--- /dev/null
+<?php
+$r = new HttpRequest('http://dev.iworks.at/.print_request.php', HTTP_POST);
+
+// if redirects is set to true, a single redirect is allowed;
+// one can set any reasonable count of allowed redirects
+$r->setOptions(
+ array( 'cookies' => array('MyCookie' => 'has a value'),
+ 'redirect' => true,
+ )
+);
+
+// common form data
+$r->setPostFields(
+ array( 'name' => 'Mike',
+ 'mail' => 'mike@php.net',
+ )
+);
+// add the file to post (form name, file name, file type)
+$r->addPostFile('image', 'profile.jpg', 'image/jpeg');
+
+try {
+ print $r->send()->getBody();
+} catch (HttpException $e) {
+ print $e;
+}
+?>
--- /dev/null
+<?php
+try {
+ $p = new HttpRequestPool;
+ // if you want to set _any_ options of the HttpRequest object,
+ // you need to do so *prior attaching* to the request pool!
+ $p->attach(new HttpRequest('http://pear.php.net', HTTP_HEAD));
+ $p->attach(new HttpRequest('http://pecl.php.net', HTTP_HEAD));
+} catch (HttpException $e) {
+ print $e;
+ exit;
+}
+
+try {
+ $p->send();
+ // HttpRequestPool implements an iterator over attached HttpRequest objects
+ foreach ($p as $r) {
+ print "Checking ", $r->getUrl(), " reported ", $r->getResponseCode(), "\n";
+ }
+} catch (HttpException $e) {
+ print $e;
+}
+?>
--- /dev/null
+<?php
+class Pool extends HttpRequestPool
+{
+ public function __construct()
+ {
+ parent::__construct(
+ new HttpRequest('http://pear.php.net', HTTP_HEAD),
+ new HttpRequest('http://pecl.php.net', HTTP_HEAD)
+ );
+
+ // HttpRequestPool methods socketPerform() and socketSelect() are
+ // protected; one could use this approach to do something else
+ // while the requests are being executed
+ print "Executing requests";
+ for ($i = 0; $this->socketPerform(); $i++) {
+ $i % 3 or print ".";
+ if (!$this->socketSelect()) {
+ throw new HttpException("Socket error!");
+ }
+ }
+ print "\nDone!\n";
+ }
+}
+
+try {
+ foreach (new Pool as $r) {
+ print "Checking ", $r->getUrl(), " reported ", $r->getResponseCode(), "\n";
+ }
+} catch (HttpException $ex) {
+ print $e;
+}
+?>
--- /dev/null
+<?php
+class FeedAggregator
+{
+ public $directory;
+ protected $feeds = array();
+
+ public function __construct($directory = 'feeds')
+ {
+ $this->setDirectory($directory);
+ }
+
+ public function setDirectory($directory)
+ {
+ $this->directory = $directory;
+ foreach (glob($this->directory .'/*.xml') as $feed) {
+ $this->feeds[basename($feed, '.xml')] = filemtime($feed);
+ }
+ }
+
+ public function url2name($url)
+ {
+ return preg_replace('/[^\w\.-]+/', '_', $url);
+ }
+
+ public function hasFeed($url)
+ {
+ return isset($this->feeds[$this->url2name($url)]);
+ }
+
+ public function addFeed($url)
+ {
+ $r = $this->setupRequest($url);
+ $r->send();
+ $this->handleResponse($r);
+ }
+
+ public function addFeeds($urls)
+ {
+ $pool = new HttpRequestPool;
+ foreach ($urls as $url) {
+ $pool->attach($this->setupRequest($url));
+ }
+ $pool->send();
+
+ foreach ($pool as $request) {
+ $this->handleResponse($request);
+ }
+ }
+
+ public function getFeed($url)
+ {
+ $this->addFeed($url);
+ return $this->loadFeed($this->url2name($url));
+ }
+
+ public function getFeeds($urls)
+ {
+ $feeds = array();
+ $this->addFeeds($urls);
+ foreach ($urls as $url) {
+ $feeds[] = $this->loadFeed($this->url2name($url));
+ }
+ return $feeds;
+ }
+
+ protected function saveFeed($file, $contents)
+ {
+ if (file_put_contents($this->directory .'/'. $file .'.xml', $contents)) {
+ $this->feeds[$file] = time();
+ } else {
+ throw new Exception("Could not save feed contents to $file.xml");
+ }
+ }
+
+ protected function loadFeed($file)
+ {
+ if (isset($this->feeds[$file]) {
+ if ($data = file_get_contents($this->directory .'/'. $file .'.xml')) {
+ return $data;
+ } else {
+ throw new Exception("Could not load feed contents from $file.xml");
+ }
+ } else {
+ throw new Exception("Unknown feed/file $file.xml");
+ }
+ }
+
+ protected function setupRequest($url)
+ {
+ $r = new HttpRequest($url);
+ $r->setOptions(array('redirect' => true));
+
+ $file = $this->url2name($url);
+
+ if (isset($this->feeds[$file])) {
+ $r->addOptions(array('lastmodified' => $this->feeds[$file]));
+ }
+
+ return $r;
+ }
+
+ protected function handleResponse(HttpRequest $r)
+ {
+ if ($r->getResponseCode() != 304) {
+ if ($r->getResponseCode() != 200) {
+ throw new Exception("Unexpected response code ". $r->getResponseCode());
+ }
+ if (!strlen($body = $r->getResponseBody())) {
+ throw new Exception("Received empty feed from ". $r->getUrl());
+ }
+ $this->saveFeed($file, $body);
+ }
+ }
+}
+?>
--- /dev/null
+<?php
+
+if (preg_match_all('/\n- ([^\n]+).*?(\<\?.+?\?\>)/s', file_get_contents($_SERVER['argv'][1]), $matches)) {
+ for ($i = 0; $i < count($matches[0]); $i++) {
+ file_put_contents(preg_replace('/\W/', '_', $matches[1][$i]).".php", $matches[2][$i]."\n");
+ }
+}
+
+?>
--- /dev/null
+
+A Beginners Tutorial
+--------------------
+$Revision$
+
+
+- GET Queries
+
+ The HttpRequest class can be used to execute any HTTP request method.
+ The following example shows a simple GET request where a few query
+ parameters are supplied. Additionally potential cookies will be
+ read from and written to a file.
+
+<?php
+$r = new HttpRequest('http://www.google.com');
+
+// store Googles cookies in a dedicated file
+$r->setOptions(
+ array( 'cookiestore' => '../cookies/google.txt',
+ )
+);
+
+$r->setQueryData(
+ array( 'q' => '+"pecl_http" -msg -cvs -list',
+ 'hl' => 'de'
+ )
+);
+
+// HttpRequest::send() returns an HttpMessage object
+// of type HttpMessage::RESPONSE or throws an exception
+try {
+ print $r->send()->getBody();
+} catch (HttpException $e) {
+ print $e;
+}
+?>
+
+- Multipart Posts
+
+ The following example shows an multipart POST request, with two form
+ fields and an image that's supposed to be uploaded to the server.
+ It's a bad habit as well as common practice to issue a redirect after
+ an received POST request, so we'll allow a redirect by enabling the
+ redirect option.
+
+<?php
+$r = new HttpRequest('http://dev.iworks.at/.print_request.php', HTTP_POST);
+
+// if redirects is set to true, a single redirect is allowed;
+// one can set any reasonable count of allowed redirects
+$r->setOptions(
+ array( 'cookies' => array('MyCookie' => 'has a value'),
+ 'redirect' => true,
+ )
+);
+
+// common form data
+$r->setPostFields(
+ array( 'name' => 'Mike',
+ 'mail' => 'mike@php.net',
+ )
+);
+// add the file to post (form name, file name, file type)
+$r->addPostFile('image', 'profile.jpg', 'image/jpeg');
+
+try {
+ print $r->send()->getBody();
+} catch (HttpException $e) {
+ print $e;
+}
+?>
+
+- Parallel Requests
+
+ It's possible to execute several HttpRequests in parallel with the
+ HttpRequestPool class. HttpRequests to send, do not need to perform
+ the same request method, but can only be attached to one HttpRequestPool
+ at the same time.
+
+<?php
+try {
+ $p = new HttpRequestPool;
+ // if you want to set _any_ options of the HttpRequest object,
+ // you need to do so *prior attaching* to the request pool!
+ $p->attach(new HttpRequest('http://pear.php.net', HTTP_HEAD));
+ $p->attach(new HttpRequest('http://pecl.php.net', HTTP_HEAD));
+} catch (HttpException $e) {
+ print $e;
+ exit;
+}
+
+try {
+ $p->send();
+ // HttpRequestPool implements an iterator over attached HttpRequest objects
+ foreach ($p as $r) {
+ print "Checking ", $r->getUrl(), " reported ", $r->getResponseCode(), "\n";
+ }
+} catch (HttpException $e) {
+ print $e;
+}
+?>
+
+- Parallel Requests?
+
+ You can use a more advanced approach by using the protected interface of
+ the HttpRequestPool class. This allows you to perform some other tasks
+ while the requests are executed.
+
+<?php
+class Pool extends HttpRequestPool
+{
+ public function __construct()
+ {
+ parent::__construct(
+ new HttpRequest('http://pear.php.net', HTTP_HEAD),
+ new HttpRequest('http://pecl.php.net', HTTP_HEAD)
+ );
+
+ // HttpRequestPool methods socketPerform() and socketSelect() are
+ // protected; one could use this approach to do something else
+ // while the requests are being executed
+ print "Executing requests";
+ for ($i = 0; $this->socketPerform(); $i++) {
+ $i % 3 or print ".";
+ if (!$this->socketSelect()) {
+ throw new HttpException("Socket error!");
+ }
+ }
+ print "\nDone!\n";
+ }
+}
+
+try {
+ foreach (new Pool as $r) {
+ print "Checking ", $r->getUrl(), " reported ", $r->getResponseCode(), "\n";
+ }
+} catch (HttpException $ex) {
+ print $e;
+}
+?>
+
+- Cached Responses
+
+ One of the main key features of HttpResponse is HTTP caching. HttpResponse
+ will calculate an ETag based on the http.etag_mode INI setting as well as
+ it will determine the last modification time of the sent entity. It uses
+ those two indicators to decide if the cache entry on the client side is
+ still valid and will emit an "304 Not Modified" response if applicable.
+
+<?php
+HttpResponse::setCacheControl('public');
+HttpResponse::setCache(true);
+HttpResponse::capture();
+
+print "This will be cached until content changes!\n";
+print "Note that this approach will only save the clients download time.\n";
+?>
+
+- Bandwidth Throttling
+
+ HttpResponse supports a basic throttling mechanism, which is enabled by
+ setting a throttle delay and a buffer size. PHP will sleep the specified
+ amount of seconds after each sent chunk of specified bytes.
+
+<?php
+// send 5000 bytes every 0.2 seconds, i.e. max ~25kByte/s
+HttpResponse::setThrottleDelay(0.2);
+HttpResponse::setBufferSize(5000);
+HttpResponse::setCache(true);
+HttpResponse::setContentType('application/x-zip');
+HttpResponse::setFile('../archive.zip');
+HttpResponse::send();
+?>
+
+Exemplar Use Cases
+------------------
+
+- KISS XMLRPC Client
+
+<?php
+class XmlRpcClient
+{
+ public $namespace;
+ protected $request;
+
+ public function __construct($url, $namespace = '')
+ {
+ $this->namespace = $namespace;
+ $this->request = new HttpRequest($url, HTTP_POST);
+ $this->request->setContentType('text/xml');
+ }
+
+ public function setOptions($options = array())
+ {
+ return $this->request->setOptions($options);
+ }
+
+ public function addOptions($options)
+ {
+ return $this->request->addOptions($options);
+ }
+
+ public function __call($method, $params)
+ {
+ if ($this->namespace) {
+ $method = $this->namespace .'.'. $method;
+ }
+ $this->request->setPostData(xmlrpc_encode_request($method, $params));
+ $response = $this->request->send();
+ if ($response->getResponseCode() != 200) {
+ throw new Exception($response->getBody(), $response->getResponseCode());
+ }
+ return xmlrpc_decode($response->getBody(), 'utf-8');
+ }
+
+ public function getHistory()
+ {
+ return $this->request->getHistory();
+ }
+}
+
+?>
+
+- Simple Feed Aggregator
+
+<?php
+class FeedAggregator
+{
+ public $directory;
+ protected $feeds = array();
+
+ public function __construct($directory = 'feeds')
+ {
+ $this->setDirectory($directory);
+ }
+
+ public function setDirectory($directory)
+ {
+ $this->directory = $directory;
+ foreach (glob($this->directory .'/*.xml') as $feed) {
+ $this->feeds[basename($feed, '.xml')] = filemtime($feed);
+ }
+ }
+
+ public function url2name($url)
+ {
+ return preg_replace('/[^\w\.-]+/', '_', $url);
+ }
+
+ public function hasFeed($url)
+ {
+ return isset($this->feeds[$this->url2name($url)]);
+ }
+
+ public function addFeed($url)
+ {
+ $r = $this->setupRequest($url);
+ $r->send();
+ $this->handleResponse($r);
+ }
+
+ public function addFeeds($urls)
+ {
+ $pool = new HttpRequestPool;
+ foreach ($urls as $url) {
+ $pool->attach($this->setupRequest($url));
+ }
+ $pool->send();
+
+ foreach ($pool as $request) {
+ $this->handleResponse($request);
+ }
+ }
+
+ public function getFeed($url)
+ {
+ $this->addFeed($url);
+ return $this->loadFeed($this->url2name($url));
+ }
+
+ public function getFeeds($urls)
+ {
+ $feeds = array();
+ $this->addFeeds($urls);
+ foreach ($urls as $url) {
+ $feeds[] = $this->loadFeed($this->url2name($url));
+ }
+ return $feeds;
+ }
+
+ protected function saveFeed($file, $contents)
+ {
+ if (file_put_contents($this->directory .'/'. $file .'.xml', $contents)) {
+ $this->feeds[$file] = time();
+ } else {
+ throw new Exception("Could not save feed contents to $file.xml");
+ }
+ }
+
+ protected function loadFeed($file)
+ {
+ if (isset($this->feeds[$file]) {
+ if ($data = file_get_contents($this->directory .'/'. $file .'.xml')) {
+ return $data;
+ } else {
+ throw new Exception("Could not load feed contents from $file.xml");
+ }
+ } else {
+ throw new Exception("Unknown feed/file $file.xml");
+ }
+ }
+
+ protected function setupRequest($url)
+ {
+ $r = new HttpRequest($url);
+ $r->setOptions(array('redirect' => true));
+
+ $file = $this->url2name($url);
+
+ if (isset($this->feeds[$file])) {
+ $r->addOptions(array('lastmodified' => $this->feeds[$file]));
+ }
+
+ return $r;
+ }
+
+ protected function handleResponse(HttpRequest $r)
+ {
+ if ($r->getResponseCode() != 304) {
+ if ($r->getResponseCode() != 200) {
+ throw new Exception("Unexpected response code ". $r->getResponseCode());
+ }
+ if (!strlen($body = $r->getResponseBody())) {
+ throw new Exception("Received empty feed from ". $r->getUrl());
+ }
+ $this->saveFeed($file, $body);
+ }
+ }
+}
+?>
+
+
HTTP_ARG_VAL(content_type, 0)
HTTP_END_ARGS;
+HTTP_EMPTY_ARGS(getRawPostData, 0);
+HTTP_BEGIN_ARGS(setRawPostData, 0, 0)
+ HTTP_ARG_VAL(raw_post_data, 0)
+HTTP_END_ARGS;
+
+HTTP_BEGIN_ARGS(addRawPostData, 0, 1)
+ HTTP_ARG_VAL(raw_post_data, 0)
+HTTP_END_ARGS;
+
HTTP_EMPTY_ARGS(getPutFile, 0);
HTTP_BEGIN_ARGS(setPutFile, 0, 0)
HTTP_ARG_VAL(filename, 0)
HTTP_REQUEST_ME(setPostFields, ZEND_ACC_PUBLIC)
HTTP_REQUEST_ME(getPostFields, ZEND_ACC_PUBLIC)
HTTP_REQUEST_ME(addPostFields, ZEND_ACC_PUBLIC)
+
+ HTTP_REQUEST_ME(setRawPostData, ZEND_ACC_PUBLIC)
+ HTTP_REQUEST_ME(getRawPostData, ZEND_ACC_PUBLIC)
+ HTTP_REQUEST_ME(addRawPostData, ZEND_ACC_PUBLIC)
HTTP_REQUEST_ME(setPostFiles, ZEND_ACC_PUBLIC)
HTTP_REQUEST_ME(addPostFile, ZEND_ACC_PUBLIC)
DCL_PROP(PROTECTED, string, url, "");
DCL_PROP(PROTECTED, string, contentType, "");
+ DCL_PROP(PROTECTED, string, rawPostData, "");
DCL_PROP(PROTECTED, string, queryData, "");
DCL_PROP(PROTECTED, string, putFile, "");
case HTTP_POST:
default:
- status = http_request_body_fill(body, Z_ARRVAL_P(GET_PROP(obj, postFields)), Z_ARRVAL_P(GET_PROP(obj, postFiles)));
+ {
+ /* check for raw post data */
+ zval *raw_data = GET_PROP(obj, rawPostData);
+
+ if (Z_STRLEN_P(raw_data)) {
+ zval *ctype = GET_PROP(obj, contentType);
+
+ if (Z_STRLEN_P(ctype)) {
+ zval **headers, *opts = GET_PROP(obj, options);
+
+ if (SUCCESS == zend_hash_find(Z_ARRVAL_P(opts), "headers", sizeof("headers"), (void **) &headers)) {
+ zval **ct_header;
+
+ /* only override if not already set */
+ if (SUCCESS != zend_hash_find(Z_ARRVAL_PP(headers), "Content-Type", sizeof("Content-Type"), (void **) &ct_header)) {
+ add_assoc_stringl(*headers, "Content-Type", Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1);
+ }
+ } else {
+ zval *headers;
+
+ MAKE_STD_ZVAL(headers);
+ array_init(headers);
+ add_assoc_stringl(headers, "Content-Type", Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1);
+ add_assoc_zval(opts, "headers", headers);
+ }
+ }
+
+ body->type = HTTP_REQUEST_BODY_CSTRING;
+ body->data = estrndup(Z_STRVAL_P(raw_data), Z_STRLEN_P(raw_data));
+ body->size = Z_STRLEN_P(raw_data);
+ } else {
+ status = http_request_body_fill(body, Z_ARRVAL_P(GET_PROP(obj, postFields)), Z_ARRVAL_P(GET_PROP(obj, postFiles)));
+ }
+ }
break;
}
INIT_PARR(obj, postFiles);
if (URL) {
- UPD_PROP(obj, string, url, URL);
+ UPD_STRL(obj, url, URL, URL_len);
}
if (meth > -1) {
UPD_PROP(obj, long, method, meth);
if (Z_TYPE_PP(opt) != IS_STRING) {
convert_to_string_ex(opt);
}
- UPD_PROP(obj, string, url, Z_STRVAL_PP(opt));
+ UPD_STRL(obj, url, Z_STRVAL_PP(opt), Z_STRLEN_PP(opt));
continue;
} else if (!strcmp(key, "method")) {
if (Z_TYPE_PP(opt) != IS_LONG) {
RETURN_FALSE;
}
- UPD_PROP(obj, string, url, URL);
+ UPD_STRL(obj, url, URL, URL_len);
RETURN_TRUE;
}
/* }}} */
RETURN_FALSE;
}
- UPD_PROP(obj, string, contentType, ctype);
+ UPD_STRL(obj, contentType, ctype, ct_len);
RETURN_TRUE;
}
/* }}} */
}
if ((!qdata) || Z_TYPE_P(qdata) == IS_NULL) {
- UPD_PROP(obj, string, queryData, "");
+ UPD_STRL(obj, queryData, "", 0);
} else if ((Z_TYPE_P(qdata) == IS_ARRAY) || (Z_TYPE_P(qdata) == IS_OBJECT)) {
char *query_data = NULL;
efree(query_data);
} else {
convert_to_string(qdata);
- UPD_PROP(obj, string, queryData, Z_STRVAL_P(qdata));
+ UPD_STRL(obj, queryData, Z_STRVAL_P(qdata), Z_STRLEN_P(qdata));
}
RETURN_TRUE;
}
{
zval *qdata, *old_qdata;
char *query_data = NULL;
+ size_t query_data_len = 0;
getObject(http_request_object, obj);
if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &qdata)) {
old_qdata = GET_PROP(obj, queryData);
- if (SUCCESS != http_urlencode_hash_ex(HASH_OF(qdata), 1, Z_STRVAL_P(old_qdata), Z_STRLEN_P(old_qdata), &query_data, NULL)) {
+ 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)) {
RETURN_FALSE;
}
- UPD_PROP(obj, string, queryData, query_data);
+ UPD_STRL(obj, queryData, query_data, query_data_len);
efree(query_data);
RETURN_TRUE;
}
/* }}} */
+/* {{{ 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.
+ */
+PHP_METHOD(HttpRequest, setRawPostData)
+{
+ char *raw_data = NULL;
+ int data_len = 0;
+ getObject(http_request_object, obj);
+
+ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &raw_data, &data_len)) {
+ RETURN_FALSE;
+ }
+
+ if (!raw_data) {
+ raw_data = "";
+ }
+
+ UPD_STRL(obj, rawPostData, raw_data, data_len);
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool HttpRequest::addRawPostData(string raw_post_data)
+ *
+ * Add raw post data.
+ * Affects only POST requests.
+ */
+PHP_METHOD(HttpRequest, addRawPostData)
+{
+ char *raw_data, *new_data;
+ int data_len;
+ getObject(http_request_object, obj);
+
+ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &raw_data, &data_len)) {
+ RETURN_FALSE;
+ }
+
+ if (data_len) {
+ zval *zdata = GET_PROP(obj, rawPostData);
+
+ new_data = emalloc(Z_STRLEN_P(zdata) + data_len + 1);
+ new_data[Z_STRLEN_P(zdata) + data_len] = '\0';
+
+ if (Z_STRLEN_P(zdata)) {
+ memcpy(new_data, Z_STRVAL_P(zdata), Z_STRLEN_P(zdata));
+ }
+
+ memcpy(new_data + Z_STRLEN_P(zdata), raw_data, data_len);
+ UPD_STRL(obj, rawPostData, new_data, Z_STRLEN_P(zdata) + data_len);
+ }
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto string HttpRequest::getRawPostData()
+ *
+ * Get previously set raw post data.
+ */
+PHP_METHOD(HttpRequest, getRawPostData)
+{
+ NO_ARGS;
+
+ IF_RETVAL_USED {
+ getObject(http_request_object, obj);
+ zval *raw_data = GET_PROP(obj, rawPostData);
+
+ RETURN_STRINGL(Z_STRVAL_P(raw_data), Z_STRLEN_P(raw_data), 1);
+ }
+}
+/* }}} */
+
/* {{{ proto bool HttpRequest::addPostFile(string name, string file[, string content_type = "application/x-octetstream"])
*
* Add a file to the POST request.
RETURN_FALSE;
}
- UPD_PROP(obj, string, putFile, file);
+ UPD_STRL(obj, putFile, file, file_len);
RETURN_TRUE;
}
/* }}} */
#define GET_STATIC_PROP(n) *GET_STATIC_PROP_EX(http_response_object_ce, n)
#define UPD_STATIC_PROP(t, n, v) UPD_STATIC_PROP_EX(http_response_object_ce, t, n, v)
#define SET_STATIC_PROP(n, v) SET_STATIC_PROP_EX(http_response_object_ce, n, v)
+#define UPD_STATIC_STRL(n, v, l) UPD_STATIC_STRL_EX(http_response_object_ce, n, v, l)
#define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpResponse, method, 0, req_args)
#define HTTP_EMPTY_ARGS(method, ret_ref) HTTP_EMPTY_ARGS_EX(HttpResponse, method, ret_ref)
http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Cache-Control '%s' doesn't match public, private or no-cache", ccontrol);
RETURN_FALSE;
} else {
- spprintf(&cctl, 0, "%s, must-revalidate, max_age=%ld", ccontrol, max_age);
- RETVAL_SUCCESS(UPD_STATIC_PROP(string, cacheControl, cctl));
+ size_t cctl_len = spprintf(&cctl, 0, "%s, must-revalidate, max_age=%ld", ccontrol, max_age);
+ RETVAL_SUCCESS(UPD_STATIC_STRL(cacheControl, cctl, cctl_len));
efree(cctl);
}
}
RETURN_FALSE;
}
- RETURN_SUCCESS(UPD_STATIC_PROP(string, contentType, ctype));
+ RETURN_SUCCESS(UPD_STATIC_STRL(contentType, ctype, ctype_len));
}
/* }}} */
{
char *file, *cd;
int file_len;
+ size_t cd_len;
zend_bool send_inline = 0;
if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &file, &file_len, &send_inline)) {
RETURN_FALSE;
}
- spprintf(&cd, 0, "%s; filename=\"%s\"", send_inline ? "inline" : "attachment", file);
- RETVAL_SUCCESS(UPD_STATIC_PROP(string, contentDisposition, cd));
+ cd_len = spprintf(&cd, 0, "%s; filename=\"%s\"", send_inline ? "inline" : "attachment", file);
+ RETVAL_SUCCESS(UPD_STATIC_STRL(contentDisposition, cd, cd_len));
efree(cd);
}
/* }}} */
RETURN_FALSE;
}
- RETURN_SUCCESS(UPD_STATIC_PROP(string, eTag, etag));
+ RETURN_SUCCESS(UPD_STATIC_STRL(eTag, etag, etag_len));
}
/* }}} */
RETURN_FALSE;
}
- if ( (SUCCESS != UPD_STATIC_PROP(string, file, the_file)) ||
+ if ( (SUCCESS != UPD_STATIC_STRL(file, the_file, file_len)) ||
(SUCCESS != UPD_STATIC_PROP(long, mode, -1))) {
RETURN_FALSE;
}
#ifndef PHP_EXT_HTTP_H
#define PHP_EXT_HTTP_H
-#define HTTP_PEXT_VERSION "0.13.0"
+#define HTTP_PEXT_VERSION "0.14.0dev"
/* make compile on Win32 */
#ifdef HTTP_HAVE_CURL
PHP_METHOD(HttpRequest, setPostFields);
PHP_METHOD(HttpRequest, getPostFields);
PHP_METHOD(HttpRequest, addPostFields);
+PHP_METHOD(HttpRequest, getRawPostData);
+PHP_METHOD(HttpRequest, setRawPostData);
+PHP_METHOD(HttpRequest, addRawPostData);
PHP_METHOD(HttpRequest, addPostFile);
PHP_METHOD(HttpRequest, setPostFiles);
PHP_METHOD(HttpRequest, getPostFiles);
# define DCL_STATIC_PROP_N(a, n) zend_declare_property_null(ce, (#n), sizeof(#n)-1, (ZEND_ACC_ ##a | ZEND_ACC_STATIC) TSRMLS_CC)
# define GET_STATIC_PROP_EX(ce, n) zend_std_get_static_property(ce, (#n), sizeof(#n)-1, 0 TSRMLS_CC)
# define UPD_STATIC_PROP_EX(ce, t, n, v) zend_update_static_property_ ##t(ce, #n, sizeof(#n)-1, (v) TSRMLS_CC)
+# define UPD_STATIC_STRL_EX(ce, n, v, l) zend_update_static_property_stringl(ce, #n, sizeof(#n)-1, (v), (l) TSRMLS_CC)
# define SET_STATIC_PROP_EX(ce, n, v) zend_update_static_property(ce, #n, sizeof(#n)-1, v TSRMLS_CC)
# define DCL_PROP(a, t, n, v) zend_declare_property_ ##t(ce, (#n), sizeof(#n)-1, (v), (ZEND_ACC_ ##a) TSRMLS_CC)
# define DCL_PROP_N(a, n) zend_declare_property_null(ce, (#n), sizeof(#n)-1, (ZEND_ACC_ ##a) TSRMLS_CC)
# define UPD_PROP(o, t, n, v) UPD_PROP_EX(o, getThis(), t, n, v)
# define UPD_PROP_EX(o, this, t, n, v) zend_update_property_ ##t(o->zo.ce, this, (#n), sizeof(#n)-1, (v) TSRMLS_CC)
+# define UPD_STRL(o, n, v, l) zend_update_property_stringl(o->zo.ce, getThis(), (#n), sizeof(#n)-1, (v), (l) TSRMLS_CC)
# define SET_PROP(o, n, z) SET_PROP_EX(o, getThis(), n, z)
# define SET_PROP_EX(o, this, n, z) zend_update_property(o->zo.ce, this, (#n), sizeof(#n)-1, (z) TSRMLS_CC)
# define GET_PROP(o, n) GET_PROP_EX(o, getThis(), n)