├── README.md └── HttpClient.php /README.md: -------------------------------------------------------------------------------- 1 | GET请求 2 | ```PHP 3 | $http = new HttpClient(); 4 | $http->set_header('User-Agent','Mozilla/5.0 (Windows NT 5.1; rv:13.0) Gecko/20100101 Firefox/13.0.1'); 5 | $http->get('http://www.example.com/'); 6 | echo $http->get_body(); 7 | ``` 8 | 9 | 10 | POST请求 11 | ```PHP 12 | $http = new HttpClient(); 13 | $http->set_header('User-Agent','Mozilla/5.0 (Windows NT 5.1; rv:13.0) Gecko/20100101 Firefox/13.0.1'); 14 | $data = array( 15 | 'id'=>1, 16 | 'name'=>'example' 17 | ); 18 | $file = array( 19 | 'photo'=>'/data/patch/file.jpg' 20 | ); 21 | $http->post('http://www.example.com/',array($data)[,$file]); 22 | echo $http->get_body(); 23 | ``` 24 | 25 | Cookie支持 26 | 27 | ```PHP 28 | $http->set_cookies($cookie); 29 | ``` 30 | 31 | 代理支持 32 | ```PHP 33 | $http->set_proxy('socks5.example.com:1080',HttpClient::PROXY_SOCKS5,'user','password'); 34 | ``` 35 | -------------------------------------------------------------------------------- /HttpClient.php: -------------------------------------------------------------------------------- 1 | 5 | */ 6 | class HttpClient { 7 | const PROXY_NONE = 0; 8 | const PROXY_SOCKS4 = 1; 9 | const PROXY_SOCKS5 = 2; 10 | const PROXY_HTTP = 4; 11 | //请求送头 12 | private $_require_header=array(); 13 | //请求Cookie信息 14 | private $_require_cookie=array(); 15 | //回发头 16 | private $_response_header=array(); 17 | //回发数据 18 | private $_response_body=''; 19 | //回发Cookie信息 20 | private $_response_cookie = array(); 21 | //回发状态码 22 | private $_response_status; 23 | //请求Uri 24 | private $_require_uri; 25 | //代理方式 26 | private $_proxy_type = HttpClient::PROXY_NONE; 27 | //代理服务器 28 | private $_proxy_host; 29 | //代理认证用户名 30 | private $_proxy_user; 31 | //代理认证密码 32 | private $_proxy_pass; 33 | //cookie持久访问 34 | private $_keep_cookie = true; 35 | 36 | private $_error; 37 | 38 | private $_mimes = array( 39 | 'gif' => 'image/gif', 40 | 'png' => 'image/png', 41 | 'bmp' => 'image/bmp', 42 | 'jpeg' => 'image/jpeg', 43 | 'pjpg' => 'image/pjpg', 44 | 'jpg' => 'image/jpeg', 45 | 'tif' => 'image/tiff', 46 | 'htm' => 'text/html', 47 | 'css' => 'text/css', 48 | 'html' => 'text/html', 49 | 'txt' => 'text/plain', 50 | 'gz' => 'application/x-gzip', 51 | 'tgz' => 'application/x-gzip', 52 | 'tar' => 'application/x-tar', 53 | 'zip' => 'application/zip', 54 | 'hqx' => 'application/mac-binhex40', 55 | 'doc' => 'application/msword', 56 | 'pdf' => 'application/pdf', 57 | 'ps' => 'application/postcript', 58 | 'rtf' => 'application/rtf', 59 | 'dvi' => 'application/x-dvi', 60 | 'latex' => 'application/x-latex', 61 | 'swf' => 'application/x-shockwave-flash', 62 | 'tex' => 'application/x-tex', 63 | 'mid' => 'audio/midi', 64 | 'au' => 'audio/basic', 65 | 'mp3' => 'audio/mpeg', 66 | 'ram' => 'audio/x-pn-realaudio', 67 | 'ra' => 'audio/x-realaudio', 68 | 'rm' => 'audio/x-pn-realaudio', 69 | 'wav' => 'audio/x-wav', 70 | 'wma' => 'audio/x-ms-media', 71 | 'wmv' => 'video/x-ms-media', 72 | 'mpg' => 'video/mpeg', 73 | 'mpga' => 'video/mpeg', 74 | 'wrl' => 'model/vrml', 75 | 'mov' => 'video/quicktime', 76 | 'avi' => 'video/x-msvideo' 77 | ); 78 | public function set_header($k,$v) { 79 | $this->_require_header[$k] = $v; 80 | } 81 | 82 | public function remove_header($k) { 83 | unset($this->_require_header); 84 | } 85 | 86 | public function set_cookie($k,$v) { 87 | $this->_require_cookie[$k] =$v; 88 | } 89 | 90 | //设置多个Cookie或者Cookie字符串 91 | public function set_cookies($v) { 92 | $this->_require_cookie = array_merge($this->_require_cookie, is_array($v) ? $v : $this->cookie_str2arr($v)); 93 | } 94 | 95 | public function get_body() { 96 | return $this->_response_body; 97 | } 98 | 99 | public function get_header() { 100 | return $this->_response_header; 101 | } 102 | 103 | public function get_cookie() { 104 | return $this->_response_cookie; 105 | } 106 | 107 | public function get_status() { 108 | return $this->_response_status; 109 | } 110 | 111 | public function set_proxy($h,$t=HttpClient::PROXY_HTTP,$u='',$p='') { 112 | $this->_proxy_host = $h; 113 | $this->_proxy_type = $t; 114 | if($u != '') { 115 | $this->_proxy_user = $u; 116 | $this->_proxy_pass = $p; 117 | } 118 | } 119 | 120 | public function keep_cookie($v){ 121 | $this->_keep_cookie = $v; 122 | } 123 | public function HttpClient() { 124 | $this->init_require(); 125 | $this->init_response(); 126 | } 127 | 128 | //将Cookie字符串转化为数组形式 129 | private function cookie_str2arr($str) { 130 | $ret = array(); 131 | $cookies = explode(';', $str); 132 | $ext = array('path','expires','domain','httponly',''); 133 | if(count($cookies)) { 134 | foreach($cookies as $cookie) { 135 | $cookie = trim($cookie); 136 | $arr = explode('=', $cookie); 137 | $value = implode('=',array_slice($arr,1,count($arr)));; 138 | $ret[trim($arr[0])] = $value; 139 | } 140 | } 141 | return $ret; 142 | } 143 | //初始化请求数据 144 | private function init_require() { 145 | $this->_require_header = array( 146 | 'Accept'=>'Accept: */*', 147 | 'Accept-Language'=>'zh-cn', 148 | 'User-Agent'=>'Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3', 149 | 'Connection'=>'close'); 150 | $this->_require_cookie = array(); 151 | } 152 | 153 | //初始化回发数据 154 | private function init_response() { 155 | $this->remove_header('Content-Type'); 156 | $this->_response_header = array(); 157 | $this->_response_body = ''; 158 | $this->_response_status = 0; 159 | if(!$this->_keep_cookie){ 160 | $this->_response_cookie = array(); 161 | } 162 | } 163 | 164 | //发送请求 165 | private function send($method,$data='') { 166 | $matches = parse_url($this->_require_uri); 167 | !isset($matches['host']) && $matches['host'] = ''; 168 | !isset($matches['path']) && $matches['path'] = ''; 169 | !isset($matches['query']) && $matches['query'] = ''; 170 | !isset($matches['port']) && $matches['port'] = ''; 171 | $host = $matches['host']; 172 | $path = $matches['path'] ? $matches['path'].($matches['query'] ? '?'.$matches['query'] : '') : '/'; 173 | $port = $matches['port'] ? $matches['port']: 80; 174 | $this->_require_header['Host']= $host.($port == 80 ? '' :(':'.$port)); 175 | 176 | if(!isset($this->_require_header['Referer'])) $this->_require_header['Referer'] = $this->_require_uri; 177 | 178 | $sock = socket_create(AF_INET,SOCK_STREAM, SOL_TCP); 179 | if(!$sock) { 180 | $this->_error = socket_last_error(); 181 | } 182 | 183 | socket_set_option($sock,SOL_SOCKET,SO_RCVTIMEO,array("sec"=>10, "usec"=>0 ) ); 184 | socket_set_option($sock,SOL_SOCKET,SO_SNDTIMEO,array("sec"=>10, "usec"=>0 ) ); 185 | 186 | if( isset($this->_proxy_type) && $this->_proxy_type != HttpClient::PROXY_NONE ) { 187 | list ($proxy_host,$proxy_port) = explode(':',$this->_proxy_host); 188 | if(!isset($proxy_port)) $proxy_port = 80; 189 | 190 | if(!@socket_connect($sock,$proxy_host,$proxy_port)) { 191 | $this->_error = "Cann't connect to {$host}:{$port}"; 192 | return false; 193 | } 194 | $host_ip = gethostbyname($host); 195 | switch($this->_proxy_type) { 196 | case HttpClient::PROXY_SOCKS4: 197 | socket_write($sock, chr(4).chr(1).pack('nN', $port,ip2long($host_ip)).'HttpClient'.chr(0)); 198 | $buf = socket_read($sock,2,PHP_BINARY_READ); 199 | if(ord(substr($buf,-1)) != 90) { 200 | $this->_error = "Request to {$host}:{$port} rejected or failed"; 201 | socket_close($sock); 202 | return false; 203 | } 204 | break; 205 | case HttpClient::PROXY_SOCKS5: 206 | //step1 207 | $auth_method = empty($this->_proxy_user) ? 1 : 2; 208 | socket_write($sock, chr(5).chr(1).chr($auth_method)); 209 | $buf = socket_read($sock,2,PHP_BINARY_READ); 210 | if(substr($buf,-1) != 0x00) { 211 | $this->_error ="Request to {$host}:{$port} rejected or failed"; 212 | socket_close($sock); 213 | return false; 214 | } 215 | //auth 216 | if($auth_method == 2) { 217 | socket_write($sock, chr(1).chr(strlen($this->_proxy_user)).$this->_proxy_user.chr(strlen($this->_proxy_pass)).$this->_proxy_pass); 218 | $buf = socket_read($sock,2,PHP_BINARY_READ); 219 | if(substr($buf,-1) != 0x00) { 220 | $this->_error = "authentication failed"; 221 | socket_close($sock); 222 | return false; 223 | } 224 | } 225 | //step2 226 | //使用代理的dns服务器 227 | socket_write($sock, pack("C5", 0x05, 0x01, 0x00, 0x03, strlen($host)).$host.pack("n", $port)); 228 | $buf = socket_read($sock,2,PHP_BINARY_READ); 229 | $response = unpack("Cversion/Cresult", $buf); 230 | if($response['result'] != 0 ) { 231 | $this->_error ="Request to {$host}:{$port} rejected or failed"; 232 | socket_close($sock); 233 | return false; 234 | } 235 | break; 236 | case HttpClient::PROXY_HTTP: 237 | $path = $this->_require_uri; 238 | $this->_require_header['Proxy-Connection'] = 'Close'; 239 | if(!empty($this->_proxy_user)) { 240 | $this->_require_header['Proxy-Authorization'] = 'Basic '.base64_encode($this->_proxy_user.':'.$this->_proxy_pass); 241 | } 242 | break; 243 | } 244 | 245 | }else { 246 | if(!socket_connect($sock,$host,$port)) { 247 | $this->_error = "Cann't connect to {$host}:{$port}"; 248 | return false; 249 | } 250 | } 251 | 252 | //send data 253 | $_method = strtoupper($method)." {$path} HTTP/1.0\r\n"; 254 | $data = $_method.$this->create_header()."\r\n".$data; 255 | 256 | socket_write($sock, $data); 257 | 258 | $this->_response_cookie = $this->_require_cookie; 259 | $recv = ''; 260 | while(($line = @socket_read($sock,1024)) != false) { 261 | $recv .= $line; 262 | } 263 | 264 | switch($this->_proxy_type) { 265 | case HttpClient::PROXY_SOCKS4: 266 | break; 267 | case HttpClient::PROXY_SOCKS5: 268 | if($recv) $recv = substr($recv,8); 269 | break; 270 | } 271 | $arr = explode("\r\n\r\n",$recv); 272 | 273 | //处理报头 274 | $heads = explode("\r\n",array_shift($arr)); 275 | 276 | foreach($heads as $line){ 277 | if(trim($line)=='' || $line=="\r\n") continue; 278 | if (!strncasecmp('HTTP', $line, 4)) { 279 | //status 280 | $status = explode(' ', $line); 281 | $this->_response_status = intval($status[1]); 282 | }elseif(!strncasecmp('Set-Cookie: ', $line, 12)) { 283 | $this->_response_cookie = array_merge($this->_response_cookie,$this->cookie_str2arr(substr($line, 12))); 284 | if($this->_keep_cookie){ 285 | $this->_require_cookie = array_merge($this->_require_cookie,$this->_response_cookie); 286 | } 287 | }else { 288 | $header = explode(':',$line,2); 289 | if(count($header) == 2) $this->_response_header[$header[0]] = trim($header[1]); 290 | } 291 | } 292 | //报文 293 | $this->_response_body = implode("\r\n\r\n",$arr); 294 | socket_close($sock); 295 | } 296 | 297 | private function create_header() { 298 | $header = ''; 299 | foreach ($this->_require_header as $k=>$v) { 300 | $header .= $k.': '.$v."\r\n"; 301 | } 302 | if(count($this->_require_cookie)) { 303 | $cookie = ''; 304 | foreach ($this->_require_cookie as $k=>$v) { 305 | $cookie .= $k.'='.$v.';'; 306 | } 307 | if(!empty($cookie)) $header .= "Cookie: $cookie\r\n"; 308 | } 309 | return $header; 310 | } 311 | 312 | //get 请求 313 | public function get($uri) { 314 | $this->_require_uri = $uri; 315 | $this->init_response(); 316 | $this->send('get'); 317 | $this->init_require(); 318 | } 319 | 320 | public function post($uri,$data=array(),$files=array()) { 321 | $this->_require_uri = $uri; 322 | $this->init_response(); 323 | $post = ''; 324 | if(count($files)) { 325 | $post = $this->post_file($data,$files); 326 | }else { 327 | $post = $this->post_text($data); 328 | } 329 | $this->_require_header['Content-Length'] = strlen($post); 330 | 331 | $this->send('post',$post); 332 | $this->init_require(); 333 | } 334 | 335 | private function post_text($data) { 336 | $post = ''; 337 | if(count($data)) { 338 | foreach($data as $k=>$v) { 339 | $post .= '&'.$this->format_post($k,$v); 340 | } 341 | $post = substr($post, 1); 342 | } 343 | $this->_require_header['Content-Type'] = 'application/x-www-form-urlencoded'; 344 | return $post; 345 | } 346 | 347 | private function post_file($data,$files) { 348 | $boundary = "---------------------------".substr(md5(rand(0,32000)),0,10); 349 | $this->_require_header['Content-Type'] = 'multipart/form-data; boundary='.$boundary; 350 | $post = "--$boundary\r\n"; 351 | //附件数据 352 | foreach($files as $k=>$v) { 353 | if(is_file($v)) { 354 | $content = file_get_contents($v); 355 | $filename = basename($v); 356 | $file_type = $this->get_mime($v); 357 | $post.="Content-Disposition: form-data; name=\"{$k}\"; filename=\"{$filename}\"\r\n"; 358 | $post.="Content-Type: {$file_type}\r\n\r\n"; 359 | $post.="$content\r\n"; 360 | $post .="--$boundary"; 361 | } 362 | } 363 | //附带文本数据 364 | if(count($data)) { 365 | foreach($data as $k=>$v) { 366 | $post .="\r\nContent-Disposition: form-data; name=\"$k\"\r\n\r\n"; 367 | $post .="$v\r\n"; 368 | $post .="--$boundary"; 369 | } 370 | } 371 | $post .="--\r\n\r\n"; 372 | return $post; 373 | } 374 | 375 | private function format_post($k,$v) { 376 | $post = ''; 377 | if(is_array($v)) { 378 | if(count($v)) { 379 | foreach($v as $_k=>$_v) { 380 | $post.= ('&'.$this->format_post($k.'['.$_k.']',$_v)); 381 | } 382 | } 383 | }else { 384 | $post.= ('&'.$k.'='.rawurlencode($v)); 385 | } 386 | return substr($post, 1); 387 | } 388 | 389 | private function get_mime($file) { 390 | $arr = explode('.', $file); 391 | $ext = strtolower($arr[count($arr)-1]); 392 | if(isset($this->_mimes[$ext])) { 393 | return $this->_mimes[$ext]; 394 | }else { 395 | return 'image/jpeg'; 396 | } 397 | } 398 | } 399 | ?> 400 | --------------------------------------------------------------------------------