<?php

/* PHP SDK
 * @version 2.0.0
 * @author magicsky0@163.com
 * @copyright © 2014, Ctrip Corporation. All rights reserved.
 */

class MyHttp
{
	public $url;
	public $method;
	public $timeout = 15;
	public $type;
	public $responed;
	
	private $_host;
	private $_uri;
	private $_port = 80;
	private $_header = array();
	private $_body = '';

	/**
	 * @param stirng $url
	 * @param stirng $method get|post
	 * @param stirng $type smart|curl|socket|fscoket
	 * @param int $timeout 
	 */
	public function __construct( $url, $type='smart', $method='post', $timeout=15)
	{
		$this->url = $url;
		$this->method = 'POST'; // strtoupper($method) TODO:暂时只支持POST
		$this->type = strtolower($type);
		$this->timeout = $timeout;
		
		$this->parse_url();
	}
	
	private function parse_url()
	{
		preg_match("/^http\:\/\/([^\:\/]+?)(\:(\d+))?\/(.+?)$/is", $this->url, $match);
		$this->_host = $match[1]; 
		$this->_uri = '/'.$match[4];
		$this->_port = !empty($match[2])?intval($match[3]):80;
	}
	
	public function set_header($request, $options = array() )
	{		
		$header['Accept'] = '*/*';
		$header['Accept-Language'] = 'zh-cn';
		$header['Accept-Encoding'] = '';
		$header['Transfer-Encoding'] = '';
		if( extension_loaded('zlib') && function_exists('gzdecode') )
		{
			$header['Accept-Encoding'] = 'gzip,deflate'; // open api服务器默认为此设置
			//$header['Transfer-Encoding'] = 'chunked'; // gzip后Content-Length长度不确定
		}
		$header['User-Agent'] = 'HttpRequest Class 1.1';
		$header['Host'] = $this->_host;
		$header['Connection'] = 'close';
		$header['Content-Length'] = strlen($request);
		$header['Content-Type'] = 'text/xml;charset=utf-8';
		
		// 允许覆盖上面的默认配置项
		if( !empty($options) )
		{
			$header = array_merge( $header, $options );
		}
		
		$this->_header = $header;
	}
	
	public function send($request)
	{		
		debugger($request,'Request Entity');
		$this->set_header($request);
		
		switch ($this->type)
		{
			case 'stream' : $this->stream($request); break;
			case 'curl' : $this->curl($request); break;
			case 'socket' : $this->socket($request); break;
			case 'fsocket' : $this->fsocket($request); break;
			default : $this->smart($request);
		}
		
		debugger($this->responed,'Responed Entity');
		return $this->responed;
	}
	
	private function encode_respond( $buffer )
	{
		debugger($buffer,'Buffer Entity');
		
		$header = $body = '';
		$p = strpos( $buffer, "\r\n\r\n" );
		
		// 去除头部信息
		if( $p > 0 )
		{
			$header = substr( $buffer, 0, $p );
			if( $p+4 < strlen($buffer) ) 
			{
				$body = substr( $buffer, $p+4 );
			}
		}
		
		// 如果header为续传标记则继续去除头部信息
		if( $header = 'HTTP/1.1 100 Continue' )
		{
			$p = strpos( $body, "\r\n\r\n" );
			
			if( $p > 0 )
			{
				$header = substr( $body, 0, $p );
				if( $p+4 < strlen($body) ) 
				{
					$body = substr( $body, $p+4 );
				}
			}
		}
		
		/*
		$body_without_header = '';
		$p2 = strpos( $buffer, "<?xml" ); 	// 第一次出现XML标记的位置
		if( $p2 > 1 )
		{
			if( $p2-1 < strlen($buffer) )
			{
				$body_without_header = substr( $buffer,$p2-1 );
			}
		}
		*/
		try
		{
			if( $this->_header['Accept-Encoding'] == 'gzip,deflate' )
			{
				$body = @gzdecode($body);
				if( !$body ) 
				{
					trigger_error('返回体解析错误',E_USER_WARNING);
				}
			}
	
			if( $this->_header['Transfer-Encoding'] == 'chunked' )
			{
				$body = @http_chunked_decode($body);
			}
		}
		catch( Exception $e )
		{
			trigger_error($e);
		}

		return $body;
	}
	
	// 智能选择通信模式
	private function smart($request)
	{
		if ( function_exists('curl_init') )
		{
			$this->curl($request);
			return TRUE;
		}

		if ( function_exists('fsockopen') )
		{
			$this->fsocket($request);
			return TRUE;
		}

		if ( function_exists('stream_context_create') )
		{
			$this->stream($request);
			return TRUE;
		}

		trigger_error('您的系统环境不支持远程调用！',E_USER_ERROR);
	}
	
	private function stream($request)
	{	
		$header = '';
		foreach($this->_header as $keys => $value)
		{
			$header .= "$keys: $value\r\n";
		}
		
		$opts = array( 
				'http' => array( 
					'method' => $this->method, 
					'header' => $header,
					'content' => $request 
				) 
			);	
		$context = stream_context_create($opts);
		$fp = fopen($this->url, 'r', FALSE, $context);
		$buffer = stream_get_contents($fp);
		fclose($fp);
	
		$this->responed = $buffer;
	}
	
	private function curl($request)
	{
		foreach($this->_header as $keys => $value)
		{
			$header[] = "$keys:$value";
		}
		$body = $request;
		
		$ch = curl_init();
	    curl_setopt($ch, CURLOPT_URL, $this->url);
	    curl_setopt($ch, CURLOPT_PORT, $this->_port);
	    curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);  
	     
	    curl_setopt($ch, CURLOPT_HEADER, TRUE); // 启用时会将头文件的信息作为数据流输出
	    curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
	    
	    curl_setopt($ch, CURLOPT_POST, TRUE); 
	    curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
	     	    
	    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); // 在启用时，返回原生的（Raw）输出 
	    $buffer = curl_exec($ch);
	    curl_close($ch);
	    
	    $this->responed = $this->encode_respond($buffer);
	}
	
	private function socket($request)
	{
		$body = "$this->method $this->_uri HTTP/1.1\r\n";		
		foreach($this->_header as $keys => $value)
		{
			$body .= "$keys: $value\r\n";
		}
		$body .= "\r\n".$request;
		
		$fp = socket_create(AF_INET,SOCK_STREAM,0);
		socket_connect($fp, $this->_host, $this->_port); // openapi.ctrip.com
		socket_write($fp,$body);
		
		$timestart = time();
		$buffer = $tmp = '';
		do{
			$buffer .= $tmp;
			$tmp = socket_read($fp,4096);
		}
		while( $tmp && (time()-$timestart) <= $this->timeout );
		
		socket_close($fp);
		
		$this->responed = $this->encode_respond($buffer);
	}
	
	private function fsocket($request)
	{
		$body = "$this->method $this->_uri HTTP/1.1\r\n";

		foreach($this->_header as $keys => $value)
		{
			$body .= "$keys: $value\r\n";
		}
		$body .= "\r\n".$request;
		
		$fp = fsockopen($this->_host, $this->_port, $errno, $errstr, $this->timeout);
		
		fputs($fp, $body);
		
		$timestart = time();
		$buffer = '';
		while( !feof($fp) && (time()-$timestart) <= $this->timeout )
		{
			$buffer .= fgets($fp,4096);
		}
		fclose($fp);

		$this->responed = $this->encode_respond($buffer);
	}
}