Source for file REST.php
Documentation is available at REST.php
* @author Greg Beaver <cellog@php.net>
* @copyright 1997-2009 The Authors
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: REST.php 313023 2011-07-06 19:17:11Z dufuz $
* @link http://pear.php.net/package/PEAR
* @since File available since Release 1.4.0a1
* For downloading xml files
require_once 'PEAR/XMLParser.php';
* Intelligently retrieve data, following hyperlinks if necessary, and re-directing
* @author Greg Beaver <cellog@php.net>
* @copyright 1997-2009 The Authors
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version Release: 1.9.4
* @link http://pear.php.net/package/PEAR
* @since Class available since Release 1.4.0a1
function PEAR_REST(&$config, $options = array ())
$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
* @param string full URL to this resource
* @param array|falsecontents of the accept-encoding header
* @param boolean if true, xml will be returned as a string, otherwise, xml will be
* parsed using PEAR_XMLParser
function retrieveCacheFirst($url, $accept = false , $forcestring = false , $channel = false )
$cachefile = $this->config->get ('cache_dir') . DIRECTORY_SEPARATOR .
md5($url) . 'rest.cachefile';
return $this->retrieveData($url, $accept, $forcestring, $channel);
* Retrieve a remote REST resource
* @param string full URL to this resource
* @param array|falsecontents of the accept-encoding header
* @param boolean if true, xml will be returned as a string, otherwise, xml will be
* parsed using PEAR_XMLParser
function retrieveData($url, $accept = false , $forcestring = false , $channel = false )
$file = $trieddownload = false;
if (!isset ($this->_options['offline'])) {
$file = $this->downloadHttp($url, $cacheId ? $cacheId['lastChange'] : false , $accept, $channel);
if ($file->getCode () !== -9276 ) {
$file = false; // use local copy if available on socket connect error
// reset the age of the cache if the server says it was unmodified
$result = $this->saveCache($url, $ret, null , true , $cacheId);
$lastmodified = $file[1 ];
$result = $this->saveCache($url, $content, $lastmodified, false , $cacheId);
if (isset ($headers['content-type'])) {
switch ($headers['content-type']) {
if ($headers['content-type'] === 'text/plain') {
$check = substr($content, 0 , 5 );
if ($check !== '<?xml') {
$err = $parser->parse ($content);
return PEAR::raiseError('Invalid xml downloaded from "' . $url . '": ' .
$content = $parser->getData ();
$parser->parse ($content);
$content = $parser->getData ();
$result = $this->saveCache($url, $content, $lastmodified, false , $cacheId);
$cacheidfile = $this->config->get ('cache_dir') . DIRECTORY_SEPARATOR .
md5($url) . 'rest.cacheid';
$cachettl = $this->config->get ('cache_ttl');
// If cache is newer than $cachettl seconds, we use the cache!
if (time() - $cacheid['age'] < $cachettl) {
$cacheidfile = $this->config->get ('cache_dir') . DIRECTORY_SEPARATOR .
md5($url) . 'rest.cacheid';
$cachefile = $this->config->get ('cache_dir') . DIRECTORY_SEPARATOR .
md5($url) . 'rest.cachefile';
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 )
$cache_dir = $this->config->get ('cache_dir');
$d = $cache_dir . DIRECTORY_SEPARATOR . md5($url);
$cacheidfile = $d . 'rest.cacheid';
$cachefile = $d . 'rest.cachefile';
if (System::mkdir (array ('-p', $cache_dir)) === false ) {
return PEAR::raiseError(" The value of config option cache_dir ($cache_dir) is not a directory and attempts to create the directory failed." );
if ($cacheid === null && $nochange) {
'lastChange' => ($nochange ? $cacheid['lastChange'] : $lastmodified),
$cachefile_fp = @fopen($file, 'xb'); // x is the O_CREAT|O_EXCL mode
if ($cachefile_fp !== false ) { // create file
if (fwrite($cachefile_fp, $contents, $len) < $len) {
$cachefile_lstat = lstat($file);
$cachefile_fp = @fopen($file, 'wb');
$cachefile_fstat = fstat($cachefile_fp);
$cachefile_lstat['mode'] == $cachefile_fstat['mode'] &&
$cachefile_lstat['ino'] == $cachefile_fstat['ino'] &&
$cachefile_lstat['dev'] == $cachefile_fstat['dev'] &&
$cachefile_fstat['nlink'] === 1
if (fwrite($cachefile_fp, $contents, $len) < $len) {
return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $file . ' as it is symlinked to ' . $link . ' - Possible symlink attack');
* 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
function downloadHttp($url, $lastmodified = null , $accept = false , $channel = false )
// always reset , so we are clean case of error
$wasredirect = $redirect;
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 . '"');
$host = isset ($info['host']) ? $info['host'] : null;
$port = isset ($info['port']) ? $info['port'] : null;
$path = isset ($info['path']) ? $info['path'] : null;
$schema = (isset ($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http';
$proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
if ($this->config->get ('http_proxy')&&
$proxy_host = isset ($proxy['host']) ? $proxy['host'] : null;
if ($schema === '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;
$proxy_schema = (isset ($proxy['scheme']) && $proxy['scheme'] == 'https') ? 'https' : 'http';
$port = (isset ($info['scheme']) && $info['scheme'] == 'https') ? 443 : 80;
if (isset ($proxy['host'])) {
$request = " GET $url HTTP/1.1\r\n";
$request = " GET $path HTTP/1.1\r\n";
$request .= " Host: $host\r\n";
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";
$ifmodifiedsince = ($lastmodified ? " If-Modified-Since: $lastmodified\r\n" : '');
$request .= $ifmodifiedsince .
"User-Agent: PEAR/1.9.4/PHP/" . PHP_VERSION . "\r\n";
$username = $this->config->get ('username', null , $channel);
$password = $this->config->get ('password', null , $channel);
if ($username && $password) {
$request .= " Authorization: Basic $tmp\r\n";
if ($proxy_host != '' && $proxy_user != '') {
$request .= 'Proxy-Authorization: Basic ' .
$request .= 'Accept: ' . implode(', ', $accept) . "\r\n";
$request .= "Accept-Encoding:\r\n";
$request .= "Connection: close\r\n";
$fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr, 15 );
return PEAR::raiseError(" Connection to `$proxy_host:$proxy_port' failed: $errstr" , -9276 );
if ($schema === 'https') {
$host = 'ssl://' . $host;
$fp = @fsockopen($host, $port, $errno, $errstr);
return PEAR::raiseError(" Connection to `$host:$port' failed: $errstr" , $errno);
if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) {
} elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
$reply = (int) $matches[1 ];
if ($reply == 304 && ($lastmodified || ($lastmodified === false ))) {
if (!in_array($reply, array (200 , 301 , 302 , 303 , 305 , 307 ))) {
return PEAR::raiseError(" File $schema://$host:$port$path not valid (received: $line)" );
if (!isset ($headers['location'])) {
return PEAR::raiseError(" File $schema://$host:$port$path not valid (redirected but no location)" );
return PEAR::raiseError(" File $schema://$host:$port$path not valid (redirection looped more than 5 times)" );
$redirect = $wasredirect + 1;
return $this->downloadHttp($headers['location'], $lastmodified, $accept, $channel);
$length = isset ($headers['content-length']) ? $headers['content-length'] : -1;
while ($chunk = @fread($fp, 8192 )) {
if ($lastmodified === false || $lastmodified) {
if (isset ($headers['etag'])) {
$lastmodified = array ('ETag' => $headers['etag']);
if (isset ($headers['last-modified'])) {
$lastmodified['Last-Modified'] = $headers['last-modified'];
$lastmodified = $headers['last-modified'];
return array ($data, $lastmodified, $headers);
Documentation generated on Wed, 06 Jul 2011 23:31:17 +0000 by phpDocumentor 1.4.3. PEAR Logo Copyright © PHP Group 2004.
|