From e78857a010ae86f5dddb593cbe96d67e5bd6f13d Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Mon, 6 Mar 2006 16:00:50 +0000 Subject: [PATCH] - split off files from tutorial.txt into lib/*.php - prepare release v.25 --- docs/examples/extract.php | 12 -- docs/examples/tutorial.txt | 295 ------------------------------------ lib/BigGet.php | 219 ++++++++++++++++++++++++++ lib/FeedAggregator.php | 187 +++++++++++++++++++++++ lib/PgLobStream.php | 100 ++++++++++++ lib/XmlRpcClient.php | 98 ++++++++++++ package.xml | 21 ++- package2.xml | 13 +- php_http.h | 2 +- rebuild | 2 +- tests/HttpResponse_002.phpt | 2 +- tests/HttpResponse_003.phpt | 2 +- tests/HttpResponse_004.phpt | 2 +- 13 files changed, 636 insertions(+), 319 deletions(-) delete mode 100644 docs/examples/extract.php create mode 100644 lib/BigGet.php create mode 100644 lib/FeedAggregator.php create mode 100644 lib/PgLobStream.php create mode 100644 lib/XmlRpcClient.php diff --git a/docs/examples/extract.php b/docs/examples/extract.php deleted file mode 100644 index 466c544..0000000 --- a/docs/examples/extract.php +++ /dev/null @@ -1,12 +0,0 @@ -)/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"); - } -} - -?> diff --git a/docs/examples/tutorial.txt b/docs/examples/tutorial.txt index 1994f9e..8d8998d 100644 --- a/docs/examples/tutorial.txt +++ b/docs/examples/tutorial.txt @@ -173,298 +173,3 @@ HttpResponse::setContentType('application/x-zip'); HttpResponse::setFile('../archive.zip'); HttpResponse::send(); ?> - -Exemplar Use Cases ------------------- - -- KISS XMLRPC Client - -namespace = $namespace; - $this->request = new HttpRequest($url, HTTP_METH_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->setRawPostData(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 - -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($r = $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->setOptions(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($this->url2name($r->getUrl()), $body); - } - } -} -?> - -- Download a big file - -saveTo('file.bin'); -*/ - -class BigGetRequest extends HttpRequest -{ - public $id; -} - -class BigGet extends HttpRequestPool -{ - const SIZE = 1048576; - - private $url; - private $size; - private $count = 0; - private $files = array(); - - static function url($url) - { - $head = new HttpRequest($url, HttpRequest::METH_HEAD); - $headers = $head->send()->getHeaders(); - $head = null; - - if (!isset($headers['Accept-Ranges'])) { - throw new HttpException("Did not receive an Accept-Ranges header from HEAD $url"); - } - if (!isset($headers['Content-Length'])) { - throw new HttpException("Did not receive a Content-Length header from HEAD $url"); - } - - $bigget = new BigGet; - $bigget->url = $url; - $bigget->size = $headers['Content-Length']; - return $bigget; - } - - function saveTo($file) - { - $this->send(); - if ($w = fopen($file, 'wb')) { - echo "\nCopying temp files to $file ...\n"; - foreach (glob("bigget_????.tmp") as $tmp) { - echo "\t$tmp\n"; - if ($r = fopen($tmp, 'rb')) { - stream_copy_to_stream($r, $w); - fclose($r); - } - unlink($tmp); - } - fclose($w); - echo "\nDone.\n"; - } - } - - function send() - { - // use max 3 simultanous requests with a req size of 1MiB - while ($this->count < 3 && -1 != $offset = $this->getRangeOffset()) { - $this->attachNew($offset); - } - - while ($this->socketPerform()) { - if (!$this->socketSelect()) { - throw new HttpSocketException; - } - } - } - - private function attachNew($offset) - { - $stop = min($this->count * self::SIZE + self::SIZE, $this->size) - 1; - - echo "Attaching new request to get range: $offset-$stop\n"; - - $req = new BigGetRequest( - $this->url, - HttpRequest::METH_GET, - array( - 'headers' => array( - 'Range' => "bytes=$offset-$stop" - ) - ) - ); - $this->attach($req); - $req->id = $this->count++; - } - - private function getRangeOffset() - { - return ($this->size >= $start = $this->count * self::SIZE) ? $start : -1; - } - - protected function socketPerform() - { - try { - $rc = parent::socketPerform(); - } catch (HttpRequestPoolException $x) { - foreach ($x->exceptionStack as $e) { - echo $e->getMessage(), "\n"; - } - } - - foreach ($this->getFinishedRequests() as $r) { - $this->detach($r); - - if (206 != $rc = $r->getResponseCode()) { - throw new HttpException("Unexpected response code: $rc"); - } - - file_put_contents(sprintf("bigget_%04d.tmp", $r->id), $r->getResponseBody()); - - if (-1 != $offset = $this->getRangeOffset()) { - $this->attachNew($offset); - } - } - - return $rc; - } -} -?> - diff --git a/lib/BigGet.php b/lib/BigGet.php new file mode 100644 index 0000000..e61ef7e --- /dev/null +++ b/lib/BigGet.php @@ -0,0 +1,219 @@ + + * @license BSD, revised + * @version $Revision$ + */ +class BigGet extends HttpRequestPool +{ + /** + * File split size + */ + const SIZE = 1048576; + + /** + * Parallel Request count + */ + const RMAX = 5; + + /** + * Whether to output debug messages + * + * @var bool + */ + public $dbg = false; + + /** + * URL + * + * @var string + */ + private $url; + + /** + * Temp file prefix + * + * @var string + */ + private $tmp; + + /** + * Size of requested resource + * + * @var int + */ + private $size; + + /** + * Whether the requests have been sent + * + * @var bool + */ + private $sent = false; + + /** + * Request counter + * + * @var int + */ + private $count = 0; + + /** + * Static constructor + * + * @param string $url + * @param string $tmp + * @return BigGet + * @throws Exception + */ + public static function url($url, $tmp = '/tmp') + { + $head = new HttpRequest($url, HttpRequest::METH_HEAD); + $headers = $head->send()->getHeaders(); + + if (200 != $head->getResponseCode()) { + throw new HttpException("Did not receive '200 Ok' from HEAD $url"); + } + if (!isset($headers['Accept-Ranges'])) { + throw new HttpException("Did not receive an Accept-Ranges header from HEAD $url"); + } + if (!isset($headers['Content-Length'])) { + throw new HttpException("Did not receive a Content-Length header from HEAD $url"); + } + + $bigget = new BigGet; + $bigget->url = $url; + $bigget->tmp = tempnam($tmp, 'BigGet.'); + $bigget->size = $headers['Content-Length']; + return $bigget; + } + + /** + * Save the resource to a file + * + * @param string $file + * @return bool + * @throws Exception + */ + public function saveTo($file) + { + $this->sent or $this->send(); + + if ($w = fopen($this->tmp, 'wb')) { + + $this->dbg && print "\nCopying temp files to $file ...\n"; + + foreach (glob($this->tmp .".????") as $tmp) { + + $this->dbg && print "\t$tmp\n"; + + if ($r = fopen($tmp, 'rb')) { + stream_copy_to_stream($r, $w); + fclose($r); + } + unlink($tmp); + } + fclose($w); + rename($this->tmp, $file); + + $this->dbg && print "\nDone.\n"; + + return true; + } + return false; + } + + /** + * Overrides HttpRequestPool::send() + * + * @return void + * @throws Exception + */ + public function send() + { + $this->sent = true; + + // use max RMAX simultanous requests with a req size of SIZE + while ($this->count < self::RMAX && -1 != $offset = $this->getRangeOffset()) { + $this->attachNew($offset); + } + + while ($this->socketPerform()) { + if (!$this->socketSelect()) { + throw new HttpSocketException; + } + } + } + + /** + * Overrides HttpRequestPool::socketPerform() + * + * @return bool + */ + protected function socketPerform() + { + try { + $rs = parent::socketPerform(); + } catch (HttpRequestPoolException $x) { + foreach ($x->exceptionStack as $e) { + echo $e->getMessage(), "\n"; + } + } + + foreach ($this->getFinishedRequests() as $r) { + $this->detach($r); + + if (206 != $rc = $r->getResponseCode()) { + throw new HttpException("Unexpected response code: $rc"); + } + + file_put_contents(sprintf("%s.%04d", $this->tmp, $r->id), $r->getResponseBody()); + + if (-1 != $offset = $this->getRangeOffset()) { + $this->attachNew($offset); + } + } + + return $rs; + } + + private function attachNew($offset) + { + $stop = min($this->count * self::SIZE + self::SIZE, $this->size) - 1; + + $this->dbg && print "Attaching new request to get range: $offset-$stop\n"; + + $req = new BigGetRequest( + $this->url, + HttpRequest::METH_GET, + array( + 'headers' => array( + 'Range' => "bytes=$offset-$stop" + ) + ) + ); + $this->attach($req); + $req->id = $this->count++; + } + + private function getRangeOffset() + { + return ($this->size >= $start = $this->count * self::SIZE) ? $start : -1; + } +} + + +/** + * BigGet request + * @ignore + */ +class BigGetRequest extends HttpRequest +{ + public $id; +} + +?> diff --git a/lib/FeedAggregator.php b/lib/FeedAggregator.php new file mode 100644 index 0000000..de2ae0b --- /dev/null +++ b/lib/FeedAggregator.php @@ -0,0 +1,187 @@ + + * @license BSD, revised + * @package pecl/http + * @version $Revision$ + */ +class FeedAggregator +{ + /** + * Cache directory + * + * @var string + */ + public $directory; + + /** + * Feeds + * + * @var array + */ + protected $feeds = array(); + + /** + * Constructor + * + * @param string $directory + */ + public function __construct($directory = 'feeds') + { + $this->setDirectory($directory); + } + + /** + * Set cache directory + * + * @param string $directory + */ + public function setDirectory($directory) + { + $this->directory = $directory; + foreach (glob($this->directory .'/*.xml') as $feed) { + $this->feeds[basename($feed, '.xml')] = filemtime($feed); + } + } + + /** + * Strips all special chars + * + * @param string $url + * @return string + */ + public function url2name($url) + { + return preg_replace('/[^\w\.-]+/', '_', $url); + } + + /** + * Checks if $url is a known feed + * + * @param string $url + * @return bool + */ + public function hasFeed($url) + { + return isset($this->feeds[$this->url2name($url)]); + } + + /** + * Add an URL as feed + * + * @param string $url + * @return void + * @throws Exception + */ + public function addFeed($url) + { + $r = $this->setupRequest($url); + $r->send(); + $this->handleResponse($r); + } + + /** + * Add several URLs as feeds + * + * @param array $urls + * @return void + * @throws Exception + */ + public function addFeeds(array $urls) + { + $pool = new HttpRequestPool; + foreach ($urls as $url) { + $pool->attach($r = $this->setupRequest($url)); + } + $pool->send(); + + foreach ($pool as $request) { + $this->handleResponse($request); + } + } + + /** + * Load a feed (from cache) + * + * @param string $url + * @return string + * @throws Exception + */ + public function getFeed($url) + { + $this->addFeed($url); + return $this->loadFeed($this->url2name($url)); + } + + /** + * Load several feeds (from cache) + * + * @param array $urls + * @return array + * @throws Exception + */ + public function getFeeds(array $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, $escape = true) + { + $r = new HttpRequest($url); + $r->setOptions(array('redirect' => true)); + + $file = $escape ? $this->url2name($url) : $url; + + if (isset($this->feeds[$file])) { + $r->setOptions(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($this->url2name($r->getUrl()), $body); + } + } +} + +?> diff --git a/lib/PgLobStream.php b/lib/PgLobStream.php new file mode 100644 index 0000000..b6927bf --- /dev/null +++ b/lib/PgLobStream.php @@ -0,0 +1,100 @@ + + * // GET /image.php?image=1234 + * if (PgLobStream::$loId = (int) $_GET['image']) { + * if ($lob = fopen('pglob: dbname=database user=mike', 'r')) { + * HttpResponse::setContentType('image/jpeg'); + * HttpResponse::setStream($lob); + * HttpResponse::send(); + * } + * } + * + * + * @copyright Michael Wallner, + * @license BSD, revised + * @package pecl/http + * @version $Revision$ + */ +class PgLobStream +{ + private $dbh; + private $loh; + private $lon; + private $size = 0; + + public static $loId; + + function stream_open($path, $mode) + { + $path = trim(parse_url($path, URL_PATH)); + + if ($path) { + if ($this->dbh = pg_connect($path)) { + if (pg_query($this->dbh, 'BEGIN')) { + if (is_resource($this->loh = pg_lo_open($this->dbh, $this->lon = self::$loId, $mode))) { + pg_lo_seek($this->loh, 0, PGSQL_SEEK_END); + $this->size = (int) pg_lo_tell($this->loh); + pg_lo_seek($this->loh, 0, PGSSQL_SEEK_SET); + return true; + } + } + } + } + return false; + } + + function stream_read($length) + { + return pg_lo_read($this->loh, $length); + } + + function stream_seek($offset, $whence = SEEK_SET) + { + return pg_lo_seek($this->loh, $offset, $whence); + } + + function stream_tell() + { + return pg_lo_tell($this->loh); + } + + function stream_eof() + { + return pg_lo_tell($this->loh) >= $this->size; + } + + function stream_flush() + { + return true; + } + + function stream_stat() + { + return array('size' => $this->size, 'ino' => $this->lon); + } + + function stream_write($data) + { + return pg_lo_write($this->loh, $data); + } + + function stream_close() + { + if (pg_lo_close($this->loh)) { + return pg_query($this->dbh, 'COMMIT'); + } else { + pg_query($this->dbh, 'ROLLBACK'); + return false; + } + } +} + +stream_register_wrapper('pglob', 'PgLobStream'); + +?> diff --git a/lib/XmlRpcClient.php b/lib/XmlRpcClient.php new file mode 100644 index 0000000..f36347d --- /dev/null +++ b/lib/XmlRpcClient.php @@ -0,0 +1,98 @@ + + * listdomain(array('domain' => 'example.com')); + * } catch (Exception $ex) { + * echo $ex; + * } + * ?> + * + * + * @copyright Michael Wallner, + * @license BSD, revised + * @package pecl/http + * @version $Revision$ + */ +class XmlRpcClient +{ + /** + * RPC namespace + * + * @var string + */ + public $namespace; + + /** + * HttpRequest instance + * + * @var HttpRequest + */ + protected $request; + + /** + * Constructor + * + * @param string $url RPC endpoint + * @param string $namespace RPC namespace + */ + public function __construct($url, $namespace = '') + { + $this->namespace = $namespace; + $this->request = new HttpRequest($url, HTTP_METH_POST); + $this->request->setContentType('text/xml'); + } + + /** + * Proxy to HttpRequest::setOptions() + * + * @param array $options + * @return unknown + */ + public function setOptions(array $options = null) + { + return $this->request->setOptions($options); + } + + /** + * Get associated HttpRequest instance + * + * @return HttpRequest + */ + public function getRequest() + { + return $this->request; + } + + /** + * RPC method proxy + * + * @param string $method RPC method name + * @param array $params RPC method arguments + * @return mixed decoded RPC response + * @throws Exception + */ + public function __call($method, array $params) + { + if ($this->namespace) { + $method = $this->namespace .'.'. $method; + } + $this->request->setRawPostData(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'); + } +} + +?> diff --git a/package.xml b/package.xml index 4342a48..df1a694 100644 --- a/package.xml +++ b/package.xml @@ -30,12 +30,17 @@ HttpResponse - 0.24.1 - 2006-02-23 + 0.25.0 + 2006-03-06 BSD, revised beta - * Fixed bug #6861 - 5 digit ports get truncated -* Fixed bug with non-functional HttpRequest::setContentType() + * Fixed bug #6924 (Linking fails on Mac OSX). +* Fixed HttpRequest::addRawPostData(). + ++ Added feature request http_put_data() and HttpRequest::(set|get|add)PutData(). ++ Added 'range' request option. ++ Added 'proxytype' request option. ++ Added HTTP_PROXY_HTTP, HTTP_PROXY_SOCKS4, HTTP_PROXY_SOCKS5 constants. @@ -49,12 +54,17 @@ HttpResponse - + + + + + + @@ -154,6 +164,7 @@ HttpResponse + diff --git a/package2.xml b/package2.xml index ec7504b..0a7b5f6 100644 --- a/package2.xml +++ b/package2.xml @@ -62,7 +62,6 @@ HttpResponse - @@ -132,6 +131,13 @@ HttpResponse + + + + + + + @@ -299,8 +305,11 @@ HttpResponse - + + + + diff --git a/php_http.h b/php_http.h index b327d22..9516a7c 100644 --- a/php_http.h +++ b/php_http.h @@ -15,7 +15,7 @@ #ifndef PHP_EXT_HTTP_H #define PHP_EXT_HTTP_H -#define PHP_EXT_HTTP_VERSION "0.25.0dev" +#define PHP_EXT_HTTP_VERSION "0.25.0" #ifdef HAVE_CONFIG_H # include "config.h" diff --git a/rebuild b/rebuild index fe6576e..b7f97c8 100755 --- a/rebuild +++ b/rebuild @@ -4,7 +4,7 @@ rm -f warnings if test -d "$1"; then PREFIX=$1 else - PREFIX=/opt + PREFIX=`dirname $(dirname $(which php-config))` fi echo "Using prefix '$PREFIX' for phpize and php-config!" diff --git a/tests/HttpResponse_002.phpt b/tests/HttpResponse_002.phpt index 72c22bc..49ad6fa 100644 --- a/tests/HttpResponse_002.phpt +++ b/tests/HttpResponse_002.phpt @@ -5,7 +5,7 @@ HttpResponse - send gzipped file include 'skip.inc'; checkmin(5); checkcgi(); -checkext('zlib'); +skipif(!http_support(HTTP_SUPPORT_ENCODINGS), "need zlib support"); ?> --ENV-- HTTP_ACCEPT_ENCODING=gzip diff --git a/tests/HttpResponse_003.phpt b/tests/HttpResponse_003.phpt index 960c0c5..9780dda 100644 --- a/tests/HttpResponse_003.phpt +++ b/tests/HttpResponse_003.phpt @@ -5,7 +5,7 @@ HttpResponse - send gzipped file with caching headers include 'skip.inc'; checkmin(5); checkcgi(); -checkext('zlib'); +skipif(!http_support(HTTP_SUPPORT_ENCODINGS), "need zlib support"); ?> --ENV-- HTTP_ACCEPT_ENCODING=gzip diff --git a/tests/HttpResponse_004.phpt b/tests/HttpResponse_004.phpt index f5e768d..e8a1d0f 100644 --- a/tests/HttpResponse_004.phpt +++ b/tests/HttpResponse_004.phpt @@ -5,7 +5,7 @@ HttpResponse - send cached gzipped data include 'skip.inc'; checkcgi(); checkmin(5.1); -checkext('zlib'); +skpif(!http_support(HTTP_SUPPORT_ENCODINGS), "need zlib support"); ?> --ENV-- HTTP_ACCEPT_ENCODING=gzip -- 2.30.2