HttpClient.php

出自ProgWiki
跳至導覽 跳至搜尋

用途

  • 在 Web-Server端以PHP實做 Get 或 Post 遠端 Web-Server的表單。

程式碼

<?php
 
/*
class HttpClient
 
工作環境
 
	PHP5
	(如需使用 https 連線的話, 須在php上先裝 OpenSSL模組)
 
參考資料
 
	RFC 2616
		Hypertext Transfer Protocol -- HTTP/1.1
		http://www.w3.org/Protocols/rfc2616/rfc2616.html
 
	function HttpRequest 原始出處
		http://www.webdeveloper.com/forum/archive/index.php/t-117095.html
*/
 
#常數定義
define( "CRLF", "\r\n" );
 
class HttpClient
{
	# 成員變數
	var $debug;
 
	# 建構子
	function __construct()
	{
		# 測試用
		#$this->debug = true;

		# 正式用
		 $this->debug = false;
	}
 
	# 解構子
	function __destruct()
	{
 
	}
 
 
	/*
		用途:
			Debug 用訊息
		函數傳入值:
			依傳入的參數的數量, 自動換行
	*/
	function DebugOut()
	{
		if ($this->debug == true) 
		{
			$numargs = func_num_args();
			$arg_list = func_get_args();
 
			for ($i = 0; $i < $numargs; $i++)
			{
		       	$s = $arg_list[$i];
 
				if (is_object($s))
				{
					var_dump($s);
					echo "\n";
				}
				else if (is_array($s))
				{
					print_r($s);
					echo "\n";
				}
				else
				{
					echo $s."\n";
				}
			}
		}
	}
 
 
	/*
		用途:
			Http Request用
 
		函數傳入值:
			$url = 網址
			$method = 操作模式 ('GET'或'POST')
			$data = 傳入的參數 (NULL, 或以array方式傳入)
			$additional_headers = 傳送自訂的http檔頭
			$followRedirects = 是否支援自動轉址
 
		函數傳回值:
			該網址回傳的結果 (字串的array)
	*/
	function HttpRequest( $url, $method = "GET", $data = NULL, $additional_headers = NULL, $followRedirects = true )
	{
    	# in compliance with the RFC 2616 post data will not redirected
		$method = strtoupper($method);
		$url_parsed = @parse_url($url);
		if (!@$url_parsed['scheme'])
			$url_parsed = @parse_url('http://'.$url);
 
		# debug用
		$this->DebugOut($url_parsed);
 
		# $url_parsed 的 array 展開成 $scheme, $host, $port, $user, $pass, $path, $query, $fragment
    	extract($url_parsed);
 
		# 表單資料編碼
		$FormData = NULL;
	    if(is_array($data))
    	{
       		$ampersand = '';
        	$temp = NULL;
			foreach($data as $k => $v)
	        {
				if ($k != "0")
				{
    				$temp .= $ampersand.urlencode($k).'='.urlencode($v);
					$ampersand = '&';
				}
			}
			$FormData = $temp;
		}
 
		# fix連線的Port
		if(!@$port)
		{
			if ($scheme == "https")
				$port = 443;
			else
				$port = 80;
		}
 
		if (!@$path)
			$path = '/';
		if (($method == "GET") and (@$FormData))
			$path .= (@$query) ? ("&".$FormData) : ("?".$FormData);
 
		# debug用	
		$this->DebugOut($path);	
 
		$out = "$method $path {$_SERVER['SERVER_PROTOCOL']}".CRLF;
		$out .= "Host: $host".CRLF;
 
		if ($method == "POST")
		{
			$out .= "Content-type: application/x-www-form-urlencoded".CRLF;
			$out .= "Content-length: ".@strlen($FormData).CRLF;
		}
 
		if (@$additional_headers)
		{
			if (is_array( $this->requestHeaders) )
			{
				foreach( $this->requestHeaders as $k => $v )
				{
					$out .= "$k: $v".CRLF;
				}
			}
			else
			{
				$out .= $additional_headers.CRLF;
			}
		}
 
		$out .= "Connection: Close".CRLF.CRLF;
 
		if ($method == "POST")
			$out .= $FormData.CRLF;
 
		# debug用
		$this->DebugOut($out);
 
		# https 網址連線修正
		$RemoteHost = ($scheme == "https" ? "ssl://" : "").$host;
 
		# Get the IP address for the target host.
		$address = gethostbyname($host);
 
		# debug用
		$this->DebugOut("fsockopen() Connect to Host:".$RemoteHost." (IP:".$address.") Port:".$port);
 
		if(!$fp = @fsockopen($RemoteHost, $port, $errno, $errstr, 15))
		{
			# debug用
			$this->DebugOut("fsockopen() Connection Error. Error($errno) $errstr\n");
 
			return false;
		}
		fwrite($fp, $out);
 
		$foundBody = false;
		$header = "";
		$body = "";
 
		while (!feof($fp))
		{
			$s = fgets($fp, 4096);
 
			# debug用
			$this->DebugOut($s);
 
			if ( $s == CRLF )
			{
				$foundBody = true;
				continue;
			}
			if ( $foundBody )
			{
				$body .= $s;
			}
			else
			{
 
				#echo $s;
            
				if( ($followRedirects) and (preg_match('/^Location:(.*)/i', $s, $matches) != false) )
				{
					fclose($fp);
					if (strpos(trim($matches[1]), 'http') === false)
					{
						$urlTo = trim($matches[1]);
						$urlLocation = ($scheme == 'https' ? 'https://' : 'http://').($host).(substr($urlTo, 0, 1) == '/' ? '' : '/').($urlTo);
						return $this->HttpRequest( $urlLocation );
					}
					else
						return $this->HttpRequest( trim($matches[1]) );
				}
				$header .= $s;
				if(preg_match('@HTTP[/]1[.][01x][\s]{1,}([1-5][01][0-9])[\s].*$@', $s, $matches))
				{
					$status = trim($matches[1]);
				}
			}
		}
		fclose($fp);
		return array('head' => trim($header), 'body' => trim($body), 'status' => $status);
	}
 
 
	/*
		用途:
			Http Response Header Fields拆解用
 
		函數傳入值:
			$header = Http Response Header
			例
				HTTP/1.1 200 OK
				Date: Tue, 23 Jan 2007 05:41:08 GMT
				Server: Apache/2.0.54 (Unix) DAV/2
				Last-Modified: Fri, 30 Jun 2006 07:16:29 GMT
				ETag: "28401-1f-d3c9cd40"
				Accept-Ranges: bytes
				Content-Length: 31
				Connection: close
				Content-Type: text/html
 
		函數傳回值:
			該網址回傳的結果 (字串的array)
	*/
	function parse_header($header)
	{
		$out = NULL;
		$header_data = preg_split('/\r\n/',$header);
 
		if (is_array($header_data))
		{
			foreach($header_data as $k => $v)
	        {
				$data = preg_split('/: /',$v);
				$out[$data[0]] = $data[1];
			}
		}
		return $out;
	}
 
 
 
	function GetStatusText($status)
	{
		switch ((int)$status)
		{
			case 100:
				$str = "Continue";
				break;
			case 101:
				$str = "Switching Protocols";
				break;
			case 200:
				$str = "OK";
				break;
			case 201:
				$str = "Created";
				break;
			case 202:
				$str = "Accepted";
				break;
			case 203:
				$str = "Non-Authoritative Information";
				break;
			case 204:
				$str = "No Content";
				break;
			case 205:
				$str = "Reset Content";
				break;
			case 206:
				$str = "Partial Content";
				break;
			case 300:
				$str = "Multiple Choices";
				break;
			case 301:
				$str = "Moved Permanently";
				break;
			case 302:
				$str = "Found";
				break;
			case 303:
				$str = "See Other";
				break;
			case 304:
				$str = "Not Modified";
				break;
			case 305:
				$str = "Use Proxy";
				break;
			case 307:
				$str = "Temporary Redirect";
				break;
			case 400:
				$str = "Bad Request";
				break;
			case 401:
				$str = "Unauthorized";
				break;
			case 402:
				$str = "Payment Required";
				break;
			case 403:
				$str = "Forbidden";
				break;
			case 404:
				$str = "Not Found";
				break;
			case 405:
				$str = "Method Not Allowed";
				break;
			case 406:
				$str = "Not Acceptable";
				break;
			case 407:
				$str = "Proxy Authentication Required";
				break;
			case 408:
				$str = "Request Time-out";
				break;
			case 409:
				$str = "Conflict";
				break;
			case 410:
				$str = "Gone";
				break;
			case 411:
				$str = "Length Required";
				break;
			case 412:
				$str = "Precondition Failed";
				break;
			case 413:
				$str = "Request Entity Too Large";
				break;
			case 414:
				$str = "Request-URI Too Large";
				break;
			case 415:
				$str = "Unsupported Media Type";
				break;
			case 416:
				$str = "Requested range not satisfiable";
				break;
			case 417:
				$str = "Expectation Failed";
				break;
			case 500:
				$str = "Internal Server Error";
				break;
			case 501:
				$str = "Not Implemented";
				break;
			case 502:
				$str = "Bad Gateway";
				break;
			case 503:
				$str = "Service Unavailable";
				break;
			case 504:
				$str = "Gateway Time-out";
				break;
 			case 505:
				$str = "HTTP Version not supported";
				break;
			default:
				$str = "Unknow status ".$ststus;
				break;
		}
		return $str;
	}
}
 
?>

使用範例

  • 取得 Yahoo 字典的 run 的解釋網頁
<?php
include_once('HttpClient.php');
 
$Client = new HttpClient();
$url = "http://tw.dictionary.yahoo.com/search";
$method = "GET";
$data = array('ei'     => 'UTF-8',
                 	'p' => 'run');
 
$ret = $Client->HttpRequest($url,$method, $data );
 
echo $ret['body'];
?>