HTTP Fetching in PHP Without cURL

HTTP Fetching in PHP Without cURL

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.

See also  Simple PHP .htpasswd 🔑 Manager

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;
whoami
Stefan Pejcic
Join the discussion

I enjoy constructive responses and professional comments to my posts, and invite anyone to comment or link to my site.