IngmarBoddington
9/27/2012 - 6:50 PM

PHP cURL Wrapper

PHP cURL Wrapper

<?php
namespace glowingminds;

/**
 * Simple wrapper for cURL page fetching
 * @author Ingmar Boddington (https://github.com/IngmarBoddington | http://www.glowingminds.co.uk/)
 * @author Barry Parkin (https://github.com/barryparkin | http://ba.rrypark.in)
 * @version 1.1
 * 
 * ~ Improved security of https connections to prevent man-in-the-middle atacks.
 * ~ Improved URL validation & moved into it's own method avoiding duplication
 * ~ Added option for returning headers only in fetch() method
 */
class Curl {
	
	private $_url;
	private $_connectionTimeout = 1; //Curl connection timeout in sec
	private $_timeout = 2; //Curl request timeout
	
	/**
	 * Setup object with target url
	 * @param string $url
	 * @throws \Exception
	 */
	public function __construct($url = NULL) {
		
		//Validate the address and set if passed and valid
		$this->validateUrl($url);
		
	}
	
	/**
	 * validate url passed
	 * @param string $url
	 * @throws \Exception
	 */
	private function validateUrl($url)
	{
	    //FILTER_VALIDATE_URL is a fairly poor validator of urls. 
	    //Until this is improved in php, additional regex check has been added...
	    if ($url) {
	        $url = filter_var($url, FILTER_VALIDATE_URL);
	        if (!$url) {
	            throw new \Exception('Invalid Address Passed to glowingminds\Curl: ' . $url);
	        }
	        //regex url checker - basic, but functional
	        if (preg_match('/^(http:|https:)\/\/([-\w])+([.-\w])*\.(\w){2,4}/i', $url) == 0) {
	            throw new \Exception('Invalid Address (Regex Fail) Passed to glowingminds\Curl: ' . $url);
	        }
	        $this->_url = $url;
	    }
	}
	
	/**
	 * Set curl connection timeout
	 * @param int $connectionTimeout
	 * @return object
         * @throws \Exception
	 */
	public function setConnectionTimeout($connectionTimeout) {
	
		//Ensure we have an int between 0 and 100
		$connectionTimeout = (int)$connectionTimeout;
		if (($connectionTimeout < 1) || ($connectionTimeout > 100)) {
			throw new \Exception('Invalid connection timeout: ' . $connectionTimeout . ', must be between 0 and 100 inclusive');
		}
		
		//Set the timeout
		$this->_connectionTimeout = $connectionTimeout;
		
		//Allow for chaining
		return $this; 
	}
	
	/**
	 * Set request timeout
	 * @param int $timeout
	 * @return object
         * @throws \Exception
	 */
	public function setTimeout($timeout) {
		
		//Ensure we have an int between 0 and 100
		$timeout = (int)$timeout;
		if (($timeout < 1) || ($timeout > 100)) {
			throw new \Exception('Invalid timeout: ' . $timeout . ', must be between 0 and 100 inclusive');
		}
		
		//Set the timeout
		$this->_timeout = $timeout;
		
		//Allow for chaining
		return $this; 
	}
	
        /**
         * Address setter
         * @param string $url
         * @return object
         * @throws \Exception
         */
	public function setAddress($url) {
		
		//Validate the address and set if passed and valid
		$this->validateUrl($url);

		
		//Allow for chaining
		return $this;
	}
	
	/**
	 * Execute curl function with options
     * @param bool $headersOnly
	 * @return string
     * @throws \Exception
	 */
	public function fetch($headersOnly = false) {
		
		//Ensure we actually have a URL to try
		if (!$this->_url) {
			throw new \Exception('No address has been set for cURL request');
		}
		
		//Make curl resource, set options
		$curl = curl_init($this->_url);
		curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $this->_connectionTimeout);
		curl_setopt($curl, CURLOPT_TIMEOUT, $this->_timeout);
		
		//cURL ships with an old pem file for certificate authentication
		//for https connections, we should really use an up to date file
		//http://curl.haxx.se/ca/cacert.pem <-- grab this one
		if(substr($this->_url, 0, 5) == 'https') {
		    if(is_file(dirname(__FILE__)."/cacert.pem")) {
		        curl_setopt ($curl, CURLOPT_CAINFO, dirname(__FILE__)."/cacert.pem");
		    } else {
		        throw new \Exception('No certificate authentication file found');
		    }
		}
		
		//check if request is for headers only
		if($headersOnly === true) {
            curl_setopt($curl, CURLOPT_HEADER, 1);
            curl_setopt($curl, CURLOPT_NOBODY, 1);
		}
		
		//Get results
		$payload = curl_exec($curl);
		if (!$payload) {
			throw new \Exception('Curl Execute Failed');
		}
		
		//Give results
		return $payload;
	}

}
?>