php封装的HttpClient,能实现些简单的功能。原来做过,这两天重新修改了一下。
<?php
/*
* Filename: httpclient.php Created on 2012-12-21 Created by RobinTang To change the template for this generated file go to Window - Preferences - PHPeclipse - PHP - Code Templates
*/
class SinCookie {
public $name; // Cookie名称
public $value; // Cookie值
// 下面三个属性现在未实现
public $expires; // 过期时间
public $path; // 路径
public $domain; // 域
// 从Cookie字符串创建一个Cookie对象
function __construct($s = false) {
if ($s) {
$i1 = strpos ( $s, '=' );
$i2 = strpos ( $s, ';' );
$this->name = trim ( substr ( $s, 0, $i1 ) );
$this->value = trim ( substr ( $s, $i1 + 1, $i2 - $i1 - 1 ) );
}
}
// 获取Cookie键值对
function getKeyValue() {
return "$this->name=$this->value";
}
}
// 会话上下文
class SinHttpContext {
public $cookies; // 会话Cookies
public $referer; // 前一个页面地址
function __construct() {
$this->cookies = array ();
$this->refrer = "";
}
// 设置Cookie
function cookie($key, $val) {
$ck = new SinCookie ();
$ck->name = $key;
$ck->value = $val;
$this->addCookie ( $ck );
}
// 添加Cookie
function addCookie($ck) {
$this->cookies [$ck->name] = $ck;
}
// 获取Cookies字串,请求时用到
function cookiesString() {
$res = '';
foreach ( $this->cookies as $ck ) {
$res .= $ck->getKeyValue () . ';';
}
return $res;
}
}
// Http请求对象
class SinHttpRequest {
public $url; // 请求地址
public $method = 'GET'; // 请求方法
public $host; // 主机
public $path; // 路径
public $scheme; // 协议,http
public $port; // 端口
public $header; // 请求头
public $body; // 请求正文
// 设置头
function setHeader($k, $v) {
if (! isset ( $this->header )) {
$this->header = array ();
}
$this->header [$k] = $v;
}
// 获取请求字符串
// 包含头和请求正文
// 获取之后直接写socket就行
function reqString() {
$matches = parse_url ( $this->url );
! isset ( $matches ['host'] ) && $matches ['host'] = '';
! isset ( $matches ['path'] ) && $matches ['path'] = '';
! isset ( $matches ['query'] ) && $matches ['query'] = '';
! isset ( $matches ['port'] ) && $matches ['port'] = '';
$host = $matches ['host'];
$path = $matches ['path'] ? $matches ['path'] . ($matches ['query'] ? '?' . $matches ['query'] : '') : '/';
$port = ! empty ( $matches ['port'] ) ? $matches ['port'] : 80;
$scheme = $matches ['scheme'] ? $matches ['scheme'] : 'http';
$this->host = $host;
$this->path = $path;
$this->scheme = $scheme;
$this->port = $port;
$method = strtoupper ( $this->method );
$res = "$method $path HTTP/1.1\r\n";
$res .= "Host: $host\r\n";
if ($this->header) {
reset ( $this->header );
while ( list ( $k, $v ) = each ( $this->header ) ) {
if (isset ( $v ) && strlen ( $v ) > 0)
$res .= "$k: $v\r\n";
}
}
$res .= "\r\n";
if ($this->body) {
$res .= $this->body;
$res .= "\r\n\r\n";
}
return $res;
}
}
// Http响应
class SinHttpResponse {
public $scheme; // 协议
public $stasus; // 状态,成功的时候是ok
public $code; // 状态码,成功的时候是200
public $header; // 响应头
public $body; // 响应正文
function __construct() {
$this->header = array ();
$this->body = null;
}
function setHeader($key, $val) {
$this->header [$key] = $val;
}
}
// HttpClient
class SinHttpClient {
public $keepcontext = true; // 是否维持会话
public $context; // 上下文
public $request; // 请求
public $response; // 响应
public $debug = false; // 是否在Debug模式,为true的时候会打印出请求内容和相同的头部
function __construct() {
$this->request = new SinHttpRequest ();
$this->response = new SinHttpResponse ();
$this->context = new SinHttpContext ();
$this->timeout = 15; // 默认的超时为15s
}
// 清除上一次的请求内容
function clearRequest() {
$this->request->body = '';
$this->request->setHeader ( 'Content-Length', false );
$this->request->setHeader ( 'Content-Type', false );
}
// post方法
// data为请求的数据
// 为键值对的时候模拟表单提交
// 其他时候为数据提交,提交的形式为xml
// 如有其他需求,请自行扩展
function post($url, $data = false) {
$this->clearRequest ();
if ($data) {
if (is_array ( $data )) {
$con = http_build_query ( $data );
$this->request->setHeader ( 'Content-Type', 'application/x-www-form-urlencoded' );
} else {
$con = $data;
$this->request->setHeader ( 'Content-Type', 'text/xml; charset=utf-8' );
}
$this->request->body = $con;
$this->request->method = "POST";
$this->request->setHeader ( 'Content-Length', strlen ( $con ) );
}
$this->startRequest ( $url );
}
// get方法
function get($url) {
$this->clearRequest ();
$this->request->method = "GET";
$this->startRequest ( $url );
}
// 该方法为内部调用方法,不用直接调用
function startRequest($url) {
$this->request->url = $url;
if ($this->keepcontext) {
// 如果保存上下文的话设置相关信息
$this->request->setHeader ( 'Referer', $this->context->refrer );
$cks = $this->context->cookiesString ();
if (strlen ( $cks ) > 0)
$this->request->setHeader ( 'Cookie', $cks );
}
// 获取请求内容
$reqstring = $this->request->reqString ();
if ($this->debug)
echo "Request:\n$reqstring\n";
try {
$fp = fsockopen ( $this->request->host, $this->request->port, $errno, $errstr, $this->timeout );
} catch ( Exception $ex ) {
echo $ex->getMessage ();
exit ( 0 );
}
if ($fp) {
stream_set_blocking ( $fp, true );
stream_set_timeout ( $fp, $this->timeout );
// 写数据
fwrite ( $fp, $reqstring );
$status = stream_get_meta_data ( $fp );
if (! $status ['timed_out']) { // 未超时
// 下面的循环用来读取响应头部
while ( ! feof ( $fp ) ) {
$h = fgets ( $fp );
if ($this->debug)
echo $h;
if ($h && ($h == "\r\n" || $h == "\n"))
break;
$pos = strpos ( $h, ':' );
if ($pos) {
$k = strtolower ( trim ( substr ( $h, 0, $pos ) ) );
$v = trim ( substr ( $h, $pos + 1 ) );
if ($k == 'set-cookie') {
// 更新Cookie
if ($this->keepcontext) {
$this->context->addCookie ( new SinCookie ( $v ) );
}
} else {
// 添加到头里面去
$this->response->setHeader ( $k, $v );
}
} else {
// 第一行数据
// 解析响应状态
$preg = '/^(\S*) (\S*) (.*)$/';
preg_match_all ( $preg, $h, $arr );
isset ( $arr [1] [0] ) & $this->response->scheme = trim ( $arr [1] [0] );
isset ( $arr [2] [0] ) & $this->response->stasus = trim ( $arr [2] [0] );
isset ( $arr [3] [0] ) & $this->response->code = trim ( $arr [3] [0] );
}
}
// 获取响应正文长度
$len = ( int ) $this->response->header ['content-length'];
$res = '';
// 下面的循环读取正文
while ( ! feof ( $fp ) && $len > 0 ) {
$c = fread ( $fp, $len );
$res .= $c;
$len -= strlen ( $c );
}
$this->response->body = $res;
}
// 关闭Socket
fclose ( $fp );
// 把返回保存到上下文维持中
$this->context->refrer = $url;
}
}
}
// demo
// now let begin test it
$client = new SinHttpClient (); // create a client
$client->get ( 'http://qzone.qq.com/' ); // get
$body = $client->response->body;
echo $body; // echo
?>