Location: PHPKode > scripts > Fast Curl > fast-curl/FastCurl.inc
<?php

/**
 * FastCurl (PHP object-oriented wrapper for {@link http://curl.haxx.se/ cURL} inspired on {@link http://jamessocol.com/projects/oocurl.php OOCurl})
 *
 * Copyright (c) 2010 Antonio López Vivar
 * 
 * LICENSE:
 * 
 * This library is free software; you can redistribute it
 * and/or modify it under the terms of the GNU Lesser General
 * Public License as published by the Free Software Foundation;
 * either version 2.1 of the License, or (at your option) any
 * later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * @category  Library
 * @package   FastCurl
 * @author    Antonio López Vivar <hide@address.com>
 * @copyright 2010 Antonio López Vivar
 * @license   http://www.opensource.org/licenses/lgpl-license.php LGPL
 * @version   1.0 
 */
	
require_once('FastCurlMulti.inc');


/**
 * @property mixed $_ch cURL handle
 * @property array $_fastcurlopt Assoc array with CURLOPTs that have been set in cURL handle.
 * @property bool $_multilock Indicates if the object is associated to a FastCurlMulti container.
 * @property string $_response Keeps the response of last curl_exec() operation.
 * @property int $_exec_mode self::FCURL_EXEC_BODY: body, self::FCURL_EXEC_HEADERS: headers, self::FCURL_EXEC_HEADERSBODY: headers + body
*/
class FastCurl implements iFastCurl
{
	const VERSION='1.0';
	
	const FCURL_EXEC_BODY=1;
	
	const FCURL_EXEC_HEADERS=2;
	
	const FCURL_EXEC_HEADERSBODY=3;
	
	const FCURL_TIMEOUT=10;
	
	private $_ch=NULL;

	private $_fastcurlopt=NULL;
	
	private $_multilock=NULL;
	
	private $_response=NULL;
	
	private $_exec_mode=NULL;

	
	/**
	* (All keys in arrays are optional and case/order insensitive).
	*
	* @param string $url
	* @param string $referrer
	* @param array $proxy array('proxy' => host:port, etc...)
	* @param array $exec_mode array('_exec_mode' => value, 'followlocation' => value, 'autoreferer' => value, 'returntransfer' => value)
	* @param array $cookies array('cookiefile' => value, 'cookiejar' => value, etc...)
	* @param array $custom_headers array('headername' => 'headervalue', ...)
	* @param int $timeout CURLOPT_TIMEOUT
	* @param array $ssl array('ssl_verifyhost' => value, 'ssl_verifypeer' => value, etc...) By default you can use HTTPS without caring about certs, keys, etc...
	* @return FastCurl $this
	*/
	public function __construct($url=NULL, $referrer=NULL, Array $proxy=NULL, Array $exec_mode=NULL, Array $cookies=NULL, Array $custom_headers=NULL, $timeout=self::FCURL_TIMEOUT, Array $ssl=NULL)
	{
		if(!extension_loaded('curl')) 
			throw new ErrorException("cURL library is not loaded. Please recompile PHP with the cURL library.");
			
		return $this->reset($url, $referrer, $proxy, $exec_mode, $cookies, $custom_headers, $timeout, $ssl);
	}
	
	
	/**
	* Use this function to FULL RESET a FastCurl object. (All keys in arrays are optional and case/order insensitive).
	*
	* @param string $url
	* @param string $referrer
	* @param array $proxy array('proxy' => host:port, etc...)
	* @param array $exec_mode array('_exec_mode' => value, 'followlocation' => value, 'autoreferer' => value, 'returntransfer' => value)
	* @param array $cookies array('cookiefile' => value, 'cookiejar' => value, etc...)
	* @param array $custom_headers array('headername' => 'headervalue', ...)
	* @param int $timeout CURLOPT_TIMEOUT
	* @param array $ssl array('ssl_verifyhost' => value, 'ssl_verifypeer' => value, etc...) By default you can use HTTPS without caring about certs, keys, etc...
	* @return FastCurl $this
	*/
	public function reset($url=NULL, $referrer=NULL, Array $proxy=NULL, Array $exec_mode=NULL, Array $cookies=NULL, Array $custom_headers=NULL, $timeout=self::FCURL_TIMEOUT, Array $ssl=NULL)
	{
		if($this->_ch)
			curl_close($this->_ch);
		
		$this->_ch=curl_init();
		$this->_fastcurlopt=array();
		$this->_response=NULL;
		$this->_multilock=NULL;
		
		$this->failnoerror=TRUE;
		$this->useragent=implode('_', array('FastCurl', self::VERSION, FastCurlMulti::VERSION));
		
		if($exec_mode)
		{
			$aux=array();
			
			foreach($exec_mode as $key => $value)
				$aux[trim(strtolower($key))]=$value;
			
			$this->set_exec_mode($aux['_exec_mode'], $aux['followlocation'], $aux['autoreferer'], $aux['returntransfer']);
		}
		else
			$this->set_exec_mode();
			
		$this->url=$url;
		$this->referer=$referrer;
		
		if($proxy)
		{
			foreach($proxy as $key => $value)
			{
				$key=strtolower(trim($key));
				$this->$key=$value;
			}
		}
	
		if($cookies)
		{
			foreach($cookies as $key => $value)
			{
				$key=strtolower(trim($key));
				$this->$key=$value;
			}
		}
		else
			$this->cookiefile='cookies';
		
		$this->httpheader=$custom_headers?$custom_headers:$this->default_headers();
		$this->timeout=is_int($timeout)?$timeout:self::FCURL_TIMEOUT;
		
		if($ssl)
		{
			foreach($ssl as $key => $value)
			{
				$key=strtolower(trim($key));
				$this->$key=$value;
			}
		}
		else
		{
			$this->ssl_verifyhost=0;
			$this->ssl_verifypeer=FALSE;
		}

		return $this;
	}
	
	
	/**
	* @return mixed $this->_ch
	*/
	public function get_ch()
	{
		return $this->_ch;
	}
	
	
	/**
	* @param int $exec_mode self::FCURL_EXEC_BODY: body, self::FCURL_EXEC_HEADERS: headers, self::FCURL_EXEC_HEADERSBODY: headers + body
	* @param bool $followloc CURLOPT_FOLLOWLOCATION
	* @param bool $autoref CURLOPT_AUTOREFERER
	* @param bool $ret_transfer CURLOPT_RETURNTRANSFER
	* @return bool TRUE
	*/
	public function set_exec_mode($exec_mode=self::FCURL_EXEC_BODY, $followloc=TRUE, $autoref=TRUE, $ret_transfer=TRUE)
	{
		switch($exec_mode)
		{
			case self::FCURL_EXEC_HEADERS:
				/* HEADERS */
				
				$this->header=TRUE;
				$this->nobody=TRUE;
				$this->_exec_mode=$exec_mode;
				
				break;
			
			case self::FCURL_EXEC_BODY:
				/* BODY */
				
				$this->header=FALSE;
				$this->nobody=FALSE;				
				$this->_exec_mode=$exec_mode;
				
				break;
			
			case self::FCURL_EXEC_HEADERSBODY:
				/* HEADERS + BODY */
			
				$this->header=TRUE;
				$this->nobody=FALSE;
				$this->_exec_mode=$exec_mode;
				
				break;

			default:
				/* BODY */
				$this->header=FALSE;
				$this->nobody=FALSE;
				$this->_exec_mode=FCURL_EXEC_BODY;
		}
		
		$this->followlocation=is_bool($followloc)?$followloc:TRUE;
		$this->autoreferer=is_bool($autoref)?$autoref:TRUE;
		$this->returntransfer=is_bool($ret_transfer)?$ret_transfer:TRUE;

		return TRUE;
	}
	
	
	/**
	* @return array array('_exec_mode' => $this->_exec_mode, 'followlocation' => $this->followlocation, 'autoreferer' => $this->autoreferer, 'returntransfer' => $this->returntransfer)
	*/
	public function get_exec_mode()
	{
		return $this->_exec_mode?array('_exec_mode' => $this->_exec_mode, 'followlocation' => $this->followlocation, 'autoreferer' => $this->autoreferer, 'returntransfer' => $this->returntransfer):NULL;
	}
	
	
	/**
	* @return FastCurlMulti $this->_multilock
	*/
	public function get_multilock()
	{
		return $this->_multilock;
	}
	
	
	/**
	* Set the fastcurlopt array. (It merges current _fastcurlopt array with the new one passed in $curlopt, overwritting values with the same key).
	*
	* @param array $curlopt array('CURLOPT_NAME' => VALUE, etc...) (Remember header's array format is array('hname' => 'hvalue', etc...)) 
	* @param bool $keep_res TRUE to keep safe current $this->_response
	* @return int $tot Number of CURLOPTs MODIFIED.
	*/
	public function set_fastcurlopt(Array $curlopt, $keep_res=TRUE)
	{
		$ncurlopt=array();
		
		foreach($curlopt as $opt => $value)
		{
			if(!preg_match('/^CURLOPT_/i', trim($opt)))
				$opt='CURLOPT_'.trim(strtoupper($opt));
			else
				$opt=trim(strtoupper($opt));
			
			if(defined($opt))
				$ncurlopt[$opt]=$value;
		}
		
		if(($tot=count($ncurlopt))>0)
		{
			if($keep_res===TRUE)
				$res=$this->_response;
			
			$this->_fastcurlopt=array_merge($this->_fastcurlopt, $ncurlopt);

			foreach($this->_fastcurlopt as $opt => $value)
			{
				$opt=strtolower(preg_replace('/^CURLOPT_(.+)$/i', '\1', $opt));
				$this->$opt=$value;
			}

			if($keep_res===TRUE)
				$this->_response=$res;
		}
		
		return $tot;
	}
	
	
	/**
	* @return array $this->_fastcurlopt
	*/
	public function get_fastcurlopt()
	{
		return $this->_fastcurlopt;
	}
	
	
	/**
	* Calls curl_exec() and returns the response.
	*
	* @param int $exec_mode
	* @param bool $followloc CURLOPT_FOLLOWLOCATION (JUST FOR THIS REQUEST)
	* @param bool $autoref CURLOPT_AUTOREFERER (JUST FOR THIS REQUEST)
	* @param bool $ret_transfer CURLOPT_RETURNTRANSFER (JUST FOR THIS REQUEST)
	* @return string|bool $this->response FRESH server's response (or FALSE if FastCurl object is locked to a FastCurlMulti container).
	*/
	public function exec($exec_mode=NULL, $followloc=TRUE, $autoref=TRUE, $ret_transfer=TRUE)
	{
		if(!$this->_multilock)
		{
			if($exec_mode)
			{
				$exec_mode_orig=$this->get_exec_mode();
				$this->set_exec_mode($exec_mode, $followloc, $autoref, $ret_transfer);
				$res=curl_exec($this->_ch);
				$this->set_exec_mode($exec_mode_orig['_exec_mode'], $exec_mode_orig['followlocation'], $exec_mode_orig['autoreferer'], $exec_mode_orig['returntransfer']);
				$this->_response=$res;
			}
			else
				$this->_response=curl_exec($this->_ch);
		}
		
		return $this->_multilock?FALSE:$this->_response;
	}
	
	
	/**
	* Returns the LAST curl_exec() or curl_multi_exec() response (Every time a CURLOPT is set with $this->opt=value, the response is reset).
	*
	* @param string $regex REGEX pattern for looking in the response.
	* @param bool $regex_all preg_match or preg_match_all?
	* @param bool $trim trim before preg_match?
	* @return string $this->response LAST server's response.
	*/
	public function fetch($regex=NULL, $regex_all=FALSE, $trim=TRUE)
	{
		if(!$this->_response && $this->_multilock)
			$this->_response=curl_multi_getcontent($this->_ch);
		else if(!$this->_response)
			$this->_response=curl_exec($this->_ch);
		
		if($regex)
		{
			$preg='preg_match'.($regex_all===TRUE?'_all':'');
			
			if(!$preg($regex, $trim?trim($this->_response):$this->_response, $ret))
				$ret=NULL;
		}
	
		return $regex?$ret:$this->_response;
	}
	
	
	/**
	* @return string curl_error 
	*/
	public function error()
	{
		return curl_error($this->_ch);
	}
	
	
	/**
	* @return int curl_errno
	*/
	public function errno()
	{
		return curl_errno($this->_ch);
	}
	
	
	/**
	* @param int $opt
	* @return mixed
	*/
	public function info($opt=NULL)
	{
		return defined('CURLINFO_'.strtoupper($opt))?curl_getinfo($this->_ch, constant('CURLINFO_'.strtoupper($opt))):curl_getinfo($this->_ch);
	}
	
	
	/**
	* Each CURLOPT is a FastCurl property (not defined), so this "magic" method is called by PHP engine every time we set a CURLOPT.
	* First we search for a constant CURLOPT_$opt and if it exists we call curl_setopt() and we store the value at $this->_fastcurlopt array
	* for retrieve with {@link __get()}
	* @param string $opt
	* @param mixed $value
	* @return bool CURLOPT was set ok
	*/
	public function __set($opt, $value)
	{
		if(defined(($const = 'CURLOPT_'.strtoupper($opt))))
		{
			switch($const)
			{
				case 'CURLOPT_HTTPHEADER':
					
					if(is_array($value) && count($value)>0)
					{
						$headers=array();
						$nvalue=array();
						
						foreach($value as $hname => $hvalue)
						{
							if(!array_key_exists(($hname=trim(strtolower($hname))), $headers))
							{
								$headers[$hname]=$hvalue;
								$nvalue[]=$hvalue?implode(': ', array($hname, trim($hvalue))):($hname.':');
							}
						}
						
						if(($ret=curl_setopt($this->_ch, constant($const), $nvalue)))
							$this->_fastcurlopt[$const]=$headers;
					}

					break;
					
				case 'CURLOPT_NOBODY':
				
					if(($ret=curl_setopt($this->_ch, constant($const), $value)))
					{
						if($value===FALSE)
						{
							if($this->post)
								$this->post=TRUE;
							else
								$this->httpget=TRUE;
						}
							
						$this->_fastcurlopt[$const]=$value;
					}
				
					break;
					
				default:
					
					if(($ret=curl_setopt($this->_ch, constant($const), $value)))
						$this->_fastcurlopt[$const]=$value;
			}
			
			//Reset _response after some CURLOPT is set
			$this->_response=NULL;
		}
		
		return $ret;
	}
	
	
	/**
	* @param string $opt
	* @return mixed CURLOPT_$opt
	*/
	public function __get($opt)
	{
		return $this->_fastcurlopt['CURLOPT_'.strtoupper($opt)];
	}
	
	
	/**
	* @param string $opt
	* @return bool CURLOPT_$opt has been set.
	*/
	public function __isset($opt)
	{
		return isset($this->_fastcurlopt['CURLOPT_'.strtoupper($opt)]);
	}

	
	/**
	* Used to "lock" the object to a FastCurlMulti container. (Remember: FastCurl N:1 FastCurlMulti)
	*
	* @param FastCurlMulti $mh
	* @return bool FastCurl object locked ok.
	*/
	public function lockMulti(FastCurlMulti $mh)
	{
		if(!$this->_multilock)
		{
			$mh->accept($this);
			$this->_response=NULL;
			$this->_multilock=$mh;
		}
		else
			$ret=FALSE;
		
		return $ret===FALSE?$ret:TRUE;
	}
	
	
	/**
	* Used to "unlock" the object to a FastCurlMulti container. (Remember: FastCurl N:1 FastCurlMulti)
	*
	* @param FastCurlMulti $mh
	* @return bool FastCurl object unlocked ok.
	*/
	public function unlockMulti(FastCurlMulti $mh)
	{
		if($this->_multilock===$mh)
		{
			$mh->release($this);
			$this->_multilock=NULL;
		}
		else
			$ret=FALSE;
		
		return $ret===FALSE?$ret:TRUE;
	}
	
	
	/**
	* If http header already exists, it will be overwritten. array('hname' => 'hvalue')
	*
	* @param array $header
	* @return bool TRUE 
	*/
	public function add_header(Array $header)
	{
		list($hname, $hvalue) = each($header);
		
		$this->httpheader=array_merge($this->_fastcurlopt['CURLOPT_HTTPHEADER'], array(trim(strtolower($hname)) => trim($hvalue)));
		
		return TRUE;
	}
	
	
	/**
	* DELETES the http header.
	* 
	* @param string $hname
	* @return bool TRUE
	*/
	public function delete_header($hname)
	{
		$this->_fastcurlopt['CURLOPT_HTTPHEADER'][trim(strtolower($hname))]=NULL;
		$this->httpheader=$this->_fastcurlopt['CURLOPT_HTTPHEADER'];
		
		return TRUE;
	}
	
	
	/**
	* Restores the default value for that http header.
	*
	* @param string $hname
	* @return bool Header reset ok.
	*/
	public function reset_header($hname)
	{
		if(($ret=(isset($this->_fastcurlopt['CURLOPT_HTTPHEADER'][trim(strtolower($hname))]) && curl_setopt($this->_ch, CURLOPT_HTTPHEADER, array()))))
		{
			unset($this->_fastcurlopt['CURLOPT_HTTPHEADER'][trim(strtolower($hname))]);
			$this->httpheader=$this->_fastcurlopt['CURLOPT_HTTPHEADER'];
		}
		
		return $ret;
	}
	
	
	/**
	* Get header's value.
	*
	* @param string $hname
	* @return string
	*/
	public function get_header($hname)
	{
		return $this->_fastcurlopt['CURLOPT_HTTPHEADER'][trim(strtolower($hname))];
	}
	
	
	/**
	* Enable POST method.
	*
	* @param bool|string|array $postdata Content-Type header will be set by cURL depending on the type of this param. Urlencoded string -> application/x-www-form-urlencoded # Array -> multipart/form-data {@link http://www.php.net/manual/es/function.curl-setopt.php More info}
	* @param string $ctype Custom content-type
	* @param string $url Updates CURLOPT_URL
	* @param string $referrer Updates CURLOPT_REFERER
	* @return bool $postdata format is correct
	*/
	public function enable_post($postdata, $ctype=NULL, $url=NULL, $referrer=NULL)
	{		
		if(is_bool($postdata))
		{
			if($postdata===FALSE)
				$this->reset_header('content-type');
				
			$this->post=$postdata;
			$this->httpget=!$postdata;
		}
		else if(!empty($postdata))
		{
			if($ctype)
				$this->add_header(array('content-type' => trim($ctype)));
			else if(isset($this->_fastcurlopt['CURLOPT_HTTPHEADER']['content-type']))
				$this->reset_header('content-type');
			
			$this->httpget=FALSE;
			$this->post=TRUE;
			$this->postfields=$ctype?$postdata:(is_array($postdata)?$postdata:trim($postdata, " \r\n&"));

			if($url)
				$this->url=$url;
		
			if($referrer)
				$this->referer=$referrer;
		}
		else
			$ret=FALSE;
		
		if($this->nobody)
			$this->nobody=TRUE;
		
		return $ret===FALSE?$ret:TRUE;
	}
	
	
	/**
	* @return array
	*/
	public function version()
 	{
 		$version = curl_version();
 		
 		$version['fastcurl_version'] = self::VERSION;
 		$version['fastcurlmulti_version'] = FastCurlMulti::VERSION;
 		
 		return $version;
 	}
	
	
	/**
	* Change it to return your default headers.
	* @return array $headers array('header1_name' => header1_value, 'header2_name' => header2_value, ...)
	*/
	public function default_headers()
	{	
		return array('Expect' => NULL);
	}
	
	
	/**
	* @param bool HTML format output
	* @param int $rows
	* @param int $cols
	*/
	public function dump($html=TRUE, $rows=50, $cols=100)
	{
		if($html)
			echo '<div><textarea rows="'.(is_int($rows)?$rows:50).'" cols="'.(is_int($cols)?$cols:100).'">';
			
		var_dump($this);
		
		if($html)
			echo '</textarea></div>';
	}
	
	
	/**
	* If the object is locked to a FastCurlMulti container we unlock it before closing curl handle.
	*/
	public function __destruct()
	{
		if($this->_multilock)
			$this->unlockMulti($this->_multilock);
		
		curl_close($this->_ch);
	}
}

interface iFastCurl
{
    function default_headers();
}

/* ?> */
Return current item: Fast Curl