Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
<?php/*** PEAR_REST** PHP versions 4 and 5** LICENSE: This source file is subject to version 3.0 of the PHP license* that is available through the world-wide-web at the following URI:* 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 web, please* send a note to license@php.net so we can mail you a copy immediately.** @category pear* @package PEAR* @author Greg Beaver <cellog@php.net>* @copyright 1997-2006 The PHP Group* @license http://www.php.net/license/3_0.txt PHP License 3.0* @version CVS: $Id: REST.php,v 1.21 2006/03/27 04:33:11 cellog Exp $* @link http://pear.php.net/package/PEAR* @since File available since Release 1.4.0a1*//*** For downloading xml files*/require_once 'PEAR.php';require_once 'PEAR/XMLParser.php';/*** Intelligently retrieve data, following hyperlinks if necessary, and re-directing* as well* @category pear* @package PEAR* @author Greg Beaver <cellog@php.net>* @copyright 1997-2006 The PHP Group* @license http://www.php.net/license/3_0.txt PHP License 3.0* @version Release: 1.5.1* @link http://pear.php.net/package/PEAR* @since Class available since Release 1.4.0a1*/class PEAR_REST{var $config;var $_options;function PEAR_REST(&$config, $options = array()){$this->config = &$config;$this->_options = $options;}/*** Retrieve REST data, but always retrieve the local cache if it is available.** This is useful for elements that should never change, such as information on a particular* release* @param string full URL to this resource* @param array|false contents of the accept-encoding header* @param boolean if true, xml will be returned as a string, otherwise, xml will be* parsed using PEAR_XMLParser* @return string|array*/function retrieveCacheFirst($url, $accept = false, $forcestring = false){$cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .md5($url) . 'rest.cachefile';if (file_exists($cachefile)) {return unserialize(implode('', file($cachefile)));}return $this->retrieveData($url, $accept, $forcestring);}/*** Retrieve a remote REST resource* @param string full URL to this resource* @param array|false contents of the accept-encoding header* @param boolean if true, xml will be returned as a string, otherwise, xml will be* parsed using PEAR_XMLParser* @return string|array*/function retrieveData($url, $accept = false, $forcestring = false){$cacheId = $this->getCacheId($url);if ($ret = $this->useLocalCache($url, $cacheId)) {return $ret;}if (!isset($this->_options['offline'])) {$trieddownload = true;$file = $this->downloadHttp($url, $cacheId ? $cacheId['lastChange'] : false, $accept);} else {$trieddownload = false;$file = false;}if (PEAR::isError($file)) {if ($file->getCode() == -9276) {$trieddownload = false;$file = false; // use local copy if available on socket connect error} else {return $file;}}if (!$file) {$ret = $this->getCache($url);if (!PEAR::isError($ret) && $trieddownload) {// reset the age of the cache if the server says it was unmodified$this->saveCache($url, $ret, null, true, $cacheId);}return $ret;}if (is_array($file)) {$headers = $file[2];$lastmodified = $file[1];$content = $file[0];} else {$content = $file;$lastmodified = false;$headers = array();}if ($forcestring) {$this->saveCache($url, $content, $lastmodified, false, $cacheId);return $content;}if (isset($headers['content-type'])) {switch ($headers['content-type']) {case 'text/xml' :case 'application/xml' :$parser = new PEAR_XMLParser;PEAR::pushErrorHandling(PEAR_ERROR_RETURN);$err = $parser->parse($content);PEAR::popErrorHandling();if (PEAR::isError($err)) {return PEAR::raiseError('Invalid xml downloaded from "' . $url . '": ' .$err->getMessage());}$content = $parser->getData();case 'text/html' :default :// use it as a string}} else {// assume XML$parser = new PEAR_XMLParser;$parser->parse($content);$content = $parser->getData();}$this->saveCache($url, $content, $lastmodified, false, $cacheId);return $content;}function useLocalCache($url, $cacheid = null){if ($cacheid === null) {$cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .md5($url) . 'rest.cacheid';if (file_exists($cacheidfile)) {$cacheid = unserialize(implode('', file($cacheidfile)));} else {return false;}}$cachettl = $this->config->get('cache_ttl');// If cache is newer than $cachettl seconds, we use the cache!if (time() - $cacheid['age'] < $cachettl) {return $this->getCache($url);}return false;}function getCacheId($url){$cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .md5($url) . 'rest.cacheid';if (file_exists($cacheidfile)) {$ret = unserialize(implode('', file($cacheidfile)));return $ret;} else {return false;}}function getCache($url){$cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .md5($url) . 'rest.cachefile';if (file_exists($cachefile)) {return unserialize(implode('', file($cachefile)));} else {return PEAR::raiseError('No cached content available for "' . $url . '"');}}/*** @param string full URL to REST resource* @param string original contents of the REST resource* @param array HTTP Last-Modified and ETag headers* @param bool if true, then the cache id file should be regenerated to* trigger a new time-to-live value*/function saveCache($url, $contents, $lastmodified, $nochange = false, $cacheid = null){$cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .md5($url) . 'rest.cacheid';$cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .md5($url) . 'rest.cachefile';if ($cacheid === null && $nochange) {$cacheid = unserialize(implode('', file($cacheidfile)));}$fp = @fopen($cacheidfile, 'wb');if (!$fp) {$cache_dir = $this->config->get('cache_dir');if (!is_dir($cache_dir)) {System::mkdir(array('-p', $cache_dir));$fp = @fopen($cacheidfile, 'wb');if (!$fp) {return false;}} else {return false;}}if ($nochange) {fwrite($fp, serialize(array('age' => time(),'lastChange' => $cacheid['lastChange'],)));fclose($fp);return true;} else {fwrite($fp, serialize(array('age' => time(),'lastChange' => $lastmodified,)));}fclose($fp);$fp = @fopen($cachefile, 'wb');if (!$fp) {if (file_exists($cacheidfile)) {@unlink($cacheidfile);}return false;}fwrite($fp, serialize($contents));fclose($fp);return true;}/*** Efficiently Download a file through HTTP. Returns downloaded file as a string in-memory* This is best used for small files** If an HTTP proxy has been configured (http_proxy PEAR_Config* setting), the proxy will be used.** @param string $url the URL to download* @param string $save_dir directory to save file in* @param false|string|array $lastmodified header values to check against for caching* use false to return the header values from this download* @param false|array $accept Accept headers to send* @return string|array Returns the contents of the downloaded file or a PEAR* error on failure. If the error is caused by* socket-related errors, the error object will* have the fsockopen error code available through* getCode(). If caching is requested, then return the header* values.** @access public*/function downloadHttp($url, $lastmodified = null, $accept = false){$info = parse_url($url);if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) {return PEAR::raiseError('Cannot download non-http URL "' . $url . '"');}if (!isset($info['host'])) {return PEAR::raiseError('Cannot download from non-URL "' . $url . '"');} else {$host = $info['host'];if (!array_key_exists('port', $info)) {$info['port'] = null;}if (!array_key_exists('path', $info)) {$info['path'] = null;}$port = $info['port'];$path = $info['path'];}$proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';if ($this->config->get('http_proxy')&&$proxy = parse_url($this->config->get('http_proxy'))) {$proxy_host = isset($proxy['host']) ? $proxy['host'] : null;if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') {$proxy_host = 'ssl://' . $proxy_host;}$proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080;$proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null;$proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null;}if (empty($port)) {if (isset($info['scheme']) && $info['scheme'] == 'https') {$port = 443;} else {$port = 80;}}If (isset($proxy['host'])) {$request = "GET $url HTTP/1.1\r\n";} else {$request = "GET $path HTTP/1.1\r\n";}$ifmodifiedsince = '';if (is_array($lastmodified)) {if (isset($lastmodified['Last-Modified'])) {$ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n";}if (isset($lastmodified['ETag'])) {$ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n";}} else {$ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : '');}$request .= "Host: $host:$port\r\n" . $ifmodifiedsince ."User-Agent: PEAR/1.5.1/PHP/" . PHP_VERSION . "\r\n";$username = $this->config->get('username');$password = $this->config->get('password');if ($username && $password) {$tmp = base64_encode("$username:$password");$request .= "Authorization: Basic $tmp\r\n";}if ($proxy_host != '' && $proxy_user != '') {$request .= 'Proxy-Authorization: Basic ' .base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";}if ($accept) {$request .= 'Accept: ' . implode(', ', $accept) . "\r\n";}$request .= "Connection: close\r\n";$request .= "\r\n";if ($proxy_host != '') {$fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr, 15);if (!$fp) {return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr",-9276);}} else {if (isset($info['scheme']) && $info['scheme'] == 'https') {$host = 'ssl://' . $host;}$fp = @fsockopen($host, $port, $errno, $errstr);if (!$fp) {return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);}}fwrite($fp, $request);$headers = array();while (trim($line = fgets($fp, 1024))) {if (preg_match('/^([^:]+):\s+(.*)\s*$/', $line, $matches)) {$headers[strtolower($matches[1])] = trim($matches[2]);} elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {if ($matches[1] == 304 && ($lastmodified || ($lastmodified === false))) {return false;}if ($matches[1] != 200) {return PEAR::raiseError("File http://$host:$port$path not valid (received: $line)", (int) $matches[1]);}}}if (isset($headers['content-length'])) {$length = $headers['content-length'];} else {$length = -1;}$data = '';while ($chunk = @fread($fp, 8192)) {$data .= $chunk;}fclose($fp);if ($lastmodified === false || $lastmodified) {if (isset($headers['etag'])) {$lastmodified = array('ETag' => $headers['etag']);}if (isset($headers['last-modified'])) {if (is_array($lastmodified)) {$lastmodified['Last-Modified'] = $headers['last-modified'];} else {$lastmodified = $headers['last-modified'];}}return array($data, $lastmodified, $headers);}return $data;}}?>