On some shared hosting accounts, cURL, fopen or file_get_contents functions may be disabled ‘for security reasons’, yet you can still achieve HTTP fetching using socket functions.
This simple class of code will allow you to do a wide range of HTTP fetching. It should be easy enough to customise should you feel the need to. Check out the 3 examples provided at the foot of the code to try it out. You should be able to use this code with plain old PHP, with no extra functionality.
class streams {
public $timeout = 5; // Time out of requests in seconds
public $defaults = array( // Default request headers
‘User-Agent’=>‘Mozilla/6.0 (Windows; Windows NT 4.0; en-US;)’,
‘Accept’=>‘*/*’,
‘Connection’=>‘Close’
);
public $request = array(); // Variable that holds the formed request
public $headers = array(); // HTTP headers of resposne
public $body = ”; // HTTP body of response
public $error = ”; // Holds a descriptive error string should something go wrong with the request
// Prepares some variables and HTTP headers for the fetch
public function request($method,$uri,$headers = array(),$post = ”) {
// Find the (mandatory) domain in the URI
$_p = parse_url($uri);
if(!isset($_p[‘scheme’])) {
$this->error = ‘URI lacks a scheme’;
return false;
}
if(!isset($_p[‘host’])) {
$this->error = ‘URI lacks a hostname’;
return false;
}
// Add the ? to any query string… that PHP likes to remove after parse_url
if(isset($_p[‘query’]))
$_p[‘query’] = ‘?’.$_p[‘query’];
else
$_p[‘query’] = ”;
if(!isset($_p[‘path’]))
$_p[‘path’] = ‘/’;
// [Request Method] [URL] [HTTP Version] in 1st line of request
array_unshift($this->request,$method.‘ ‘.$_p[‘path’].$_p[‘query’].” HTTP/1.1″);
// Host header
$this->request[‘Host’] = ‘Host: ‘.$_p[‘host’];
// Add default headers
foreach($this->defaults as $headername => $headervalue)
$this->request[$headername] = $headername.‘: ‘.$headervalue;
// Add user-defined headers
foreach($headers as $headername => $headervalue)
$this->request[$headername] = $headername.‘: ‘.$headervalue;
// If performing a POST, add some headers and the POST data
if($method == ‘POST’ && strlen($post)) {
$this->request[‘Content-Length’] = ‘Content-Length: ‘.strlen($post);
$this->request[‘Content-Type’] = ‘Content-Type: application/x-www-form-urlencoded’;
array_push($this->request,“\r\n”.$post);
}
else
array_push($this->request,“\r\n”);
// Assume port 80|443 if there is not a user defined port in the URI
if(!isset($_p[‘port’]))
$_p[‘port’] = (strtolower($_p[‘scheme’]) == ‘https’ ? 443 : 80);
// … ready to perform the connection …
// Connect to server
$fp = fsockopen(($_p[‘port’] == 443 ? ‘ssl://’ : ”).$_p[‘host’],$_p[‘port’],$errno,$this->error,$this->timeout);
if(!$fp)
return false;
// For separating the HEAD and BODY of HTTP responses
$head = true;
$body = ”;
// Send request headers
fputs($fp,implode(“\r\n”,$this->request));
// Read the response back, separate HTTP headers and body
while(!feof($fp)) {
if($head) {
if(!trim($line = fgets($fp))) {
$head = false;
if(!isset($this->headers[‘transfer-encoding’]) && !isset($this->headers[‘content-encoding’]))
$body = &$this->body;
}
else {
$line = preg_split(“‘:s*’”,rtrim($line),2);
@$this->headers[strtolower($line[0])] = $line[1];
}
}
else
$body .= fread($fp,65536);
}
// Decode the response if required to, deals with chunked transfer-encoding and gzipped content-encoding
if(isset($this->headers[‘transfer-encoding’]) || isset($this->headers[‘content-encoding’],$body))
$this->decode_body($body);
}
// Separated for clarity and extensibility
private function decode_body($body,$eol = “rn”) {
$add = strlen($eol);
if(isset($this->headers[‘transfer-encoding’]) && $this->headers[‘transfer-encoding’] == ‘chunked’) {
do {
$body = ltrim($body);
$pos = strpos($body,$eol);
$len = hexdec(substr($body,0,$pos ));
if(isset($this->headers[‘content-encoding’] ) )
$this->body .= gzinflate(substr($body,($pos + $add + 10 ),$len ));
else
$this->body .= substr($body,($pos + $add ),$len);
$body = substr($body,($len + $pos + $add ));
$check = trim($body);
} while(!empty($check));
}
elseif(isset($this->headers[‘content-encoding’]))
$this->body = gzinflate(substr($body,10));
}
}
You may find httpbin.org useful in testing your network requests.
Here are some simple examples to get started with…
Example 1 – HTTP Fetching (GET)
$_str = new streams;
$_str->request(‘GET’,‘http://httpbin.org/get’);
print_r($_str->headers);
print_r($_str->body);
echo $_str->error;
Example 2 – HTTP Fetching (POST)
$_str = new streams;
$_str->request(‘POST’,‘http://httpbin.org/post’,array(),‘zebedee=123&mytest=yes’);
print_r($_str->headers);
print_r($_str->body);
echo $_str->error;
Example 3 – Adding custom headers
$xtra = array(
‘User-Agent’=>‘My custom user agent’,
‘Cookie’=>‘SESS=1357913’
);
$_str = new streams;
$_str->request(‘GET’,‘http://httpbin.org/get’,$xtra);
print_r($_str->headers);
print_r($_str->body);
echo $_str->error;