一个PHP写的简单webservice服务端+客户端

一个PHP写的简单webservice服务端+客户端

首先是服务端,服务端有一个主要的class组成:apiServer.php

<?php
/**
 * apiServer.php
 *
 * webservice主类
 *
 * @filename apiServer.php
 * @version  v1.0
 * @update   2011-12-22
 * @author   homingway
 * @contact  homingway@gmail.com
 * @package  webservice
 */
define('API_AUTH_KEY',  'i8XsJb$fJ!87FblnW');
class apiServer{
 
    //请求参数
    public $request = array();
 
    //是否ip限制
    public $ip_limit = true;
    //允许访问的IP列表
    public $ip_allow = array('127.0.0.1','192.168.0.99');
 
    public $default_method = 'welcome.index';
    public $service_method = array();
 
    //私有静态单例变量
    private static $_instance = null;
 
    /**
     * 构造方法,处理请求参数
     */
    private function __construct(){
        $this->dealRequest();
    }
 
    /**
     * 单例运行
     */
    public static function getInstance(){
        if(self::$_instance === null){
            self::$_instance = new self();
        }
        return self::$_instance;
    }
 
    /**
     * 运行
     */
    public function run(){
        //授权
        if(!$this->checkAuth()){
            exit('3|Access Denied');
        }
        $this->getApiMethod();
        include_once(API_SERVICE_PATH.'/'.$this->service_method['service'].'.php');
        $serviceObject = new $this->service_method['service'];
        if($this->request['param']){
            $result = call_user_func_array(array($serviceObject,$this->service_method['method']),$this->request['param']);
        } else {
            $result = call_user_func(array($serviceObject,$this->service_method['method']));
        }
        if(is_array($result)){
            $result = json_encode($result);
        }
        $result = gzencode($result);
        exit($result);
    }
 
    /**
     * 检查授权
     */
    public function checkAuth(){
        //检查参数是否为空
        if(!$this->request['time'] || !$this->request['method']   || !$this->request['auth']){
            return false;
        }
 
        //检查auth是否正确
        $server_auth = md5(md5($this->request['time'].'|'.$this->request['method'].'|'.API_AUTH_KEY));
        if($server_auth != $this->request['auth']){
            return false;
        }
 
        //ip限制
        if($this->ip_limit){
            $remote_ip = $this->getIP();
            $intersect = array_intersect($remote_ip,$this->ip_allow);
            if(empty($intersect)){
                return false;
            }
        }
 
        return true;
    }
 
    /**
     * 获取服务名和方法名
     */
    public function getApiMethod(){
        if(strpos($this->request['method'], '.') === false){
            $method = $this->default_method;
        } else {
            $method = $this->request['method'];
        }
        $tmp = explode('.', $method);
        $this->service_method = array('service'=>$tmp[0],'method'=>$tmp[1]);
        return $this->service_method;
    }
 
    /**
     * 获取和处理请求参数
     */
    public function dealRequest(){
        $this->request['time'] = $this->_request('time');
        $this->request['method'] = $this->_request('method');
        $this->request['param'] = $this->_request('param');
        $this->request['auth'] = $this->_request('auth');
        if($this->request['param']){
            $this->request['param'] = json_decode(urldecode($this->request['param']),true);
        }
    }
 
    /**
     * 获取request变量
     * @param string $item
     */
    private function _request($item){
        return isset($_REQUEST[$item]) ? trim($_REQUEST[$item]) : '';
    }
 
    /**
     * 设置IP限制
     * @param bool $limit
     */
    public function setIPLimit($limit=true){
        $this->ip_limit = $limit;
    }
 
    /**
     * 获取客户端ip地址
     */
    public function getIP(){
        $ip = array();
        if(isset($_SERVER['REMOTE_ADDR'])){
            $ip[] = $_SERVER['REMOTE_ADDR'];
        }
        if(isset($_SERVER['HTTP_VIA'])){
            $tmp = explode(', ',$_SERVER['HTTP_X_FORWARDED_FOR']);
            $ip = array_merge($ip,$tmp);
        }
        $ip = array_unique($ip);
        return $ip;
    }
 
}
?>

然后在服务端的入口文件中调用该class,并启动服务即可,如:

<?php
/**
 * server.php
 *
 * 自定义数据接口的入口
 *
 * @filename server.php
 * @version  v1.0
 * @update   2011-12-22
 * @author   homingway
 * @contact  homingway@gmail.com
 * @package  webservice
 */
 
//API的根目录
define('API_PATH',dirname(__FILE__));
 
//服务目录
define('API_SERVICE_PATH',API_PATH.'/service');
define('API_LIB_PATH',  API_PATH.'/lib');
 
//服务核心class
include_once(API_LIB_PATH.'/apiServer.php');
 
//运行
apiServer::getInstance()->run();
?>

然后创建一个service的目录,里面就是自己的接口class,如welcome.php:

<?php
/**
 * welcome.php
 *
 * 功能代码
 *
 * @filename welcome.php
 * @version  v1.0
 * @update   2011-12-22
 * @author   homingway
 * @contact  homingway@gmail.com
 * @package  webservice
 */
 
class welcome{
 
    public function index(){
        return 'hello service';
    }
 
}
?>

下面是客户端的主程序:apiClient.php

<?php
/**
 * apiClient.php
 *
 * webservice客户端程序
 *
 * @filename apiClient.php
 * @version  v1.0
 * @update   2011-12-22
 * @author   homingway
 * @contact  homingway@gmail.com
 * @package  webservice
 */
 
define('API_AUTH_KEY',  'i8XsJb$fJ!87FblnW');
 
class apiClient{
 
    public static function send($url,$method,$param=array()){
        $time = time();
        $auth = md5(md5($time.'|'.$method.'|'.API_AUTH_KEY));
        if(!is_array($param) || empty($param)){
            $json_param = '';
        } else {
            $json_param = urlencode(json_encode($param));
        }
        $api_url = $url.'?method='.$method.'&time='.$time.'&auth='.$auth.'&param='.$json_param;
        $content = file_get_contents($api_url);
        if(function_exists('gzdecode')){
            $content = gzdecode($content);
        } else {
            $content = self::gzdecode($content);
        }
        return $content;
    }
 
    public static function gzdecode($data) {
        $len = strlen ( $data );
        if ($len < 18 || strcmp ( substr ( $data, 0, 2 ), "x1fx8b" )) {
            return null; // Not GZIP format (See RFC 1952)
        }
        $method = ord ( substr ( $data, 2, 1 ) ); // Compression method
        $flags = ord ( substr ( $data, 3, 1 ) ); // Flags
        if ($flags & 31 != $flags) {
            // Reserved bits are set -- NOT ALLOWED by RFC 1952
            return null;
        }
        // NOTE: $mtime may be negative (PHP integer limitations)
        $mtime = unpack ( "V", substr ( $data, 4, 4 ) );
        $mtime = $mtime [1];
        $xfl = substr ( $data, 8, 1 );
        $os = substr ( $data, 8, 1 );
        $headerlen = 10;
        $extralen = 0;
        $extra = "";
        if ($flags & 4) {
            // 2-byte length prefixed EXTRA data in header
            if ($len - $headerlen - 2 < 8) {
                return false; // Invalid format
            }
            $extralen = unpack ( "v", substr ( $data, 8, 2 ) );
            $extralen = $extralen [1];
            if ($len - $headerlen - 2 - $extralen < 8) {
                return false; // Invalid format
            }
            $extra = substr ( $data, 10, $extralen );
            $headerlen += 2 + $extralen;
        }
        $filenamelen = 0;
        $filename = "";
        if ($flags & 8) {
            // C-style string file NAME data in header
            if ($len - $headerlen - 1 < 8) {
                return false; // Invalid format
            }
            $filenamelen = strpos ( substr ( $data, 8 + $extralen ), chr ( 0 ) );
            if ($filenamelen === false || $len - $headerlen - $filenamelen - 1 < 8) {
                return false; // Invalid format
            }
            $filename = substr ( $data, $headerlen, $filenamelen );
            $headerlen += $filenamelen + 1;
        }
 
        $commentlen = 0;
        $comment = "";
        if ($flags & 16) {
            // C-style string COMMENT data in header
            if ($len - $headerlen - 1 < 8) {
                return false; // Invalid format
            }
            $commentlen = strpos ( substr ( $data, 8 + $extralen + $filenamelen ), chr ( 0 ) );
            if ($commentlen === false || $len - $headerlen - $commentlen - 1 < 8) {
                return false; // Invalid header format
            }
            $comment = substr ( $data, $headerlen, $commentlen );
            $headerlen += $commentlen + 1;
        }
 
        $headercrc = "";
        if ($flags & 1) {
            // 2-bytes (lowest order) of CRC32 on header present
            if ($len - $headerlen - 2 < 8) {
                return false; // Invalid format
            }
            $calccrc = crc32 ( substr ( $data, 0, $headerlen ) ) & 0xffff;
            $headercrc = unpack ( "v", substr ( $data, $headerlen, 2 ) );
            $headercrc = $headercrc [1];
            if ($headercrc != $calccrc) {
                return false; // Bad header CRC
            }
            $headerlen += 2;
        }
 
        // GZIP FOOTER - These be negative due to PHP's limitations
        $datacrc = unpack ( "V", substr ( $data, - 8, 4 ) );
        $datacrc = $datacrc [1];
        $isize = unpack ( "V", substr ( $data, - 4 ) );
        $isize = $isize [1];
 
        // Perform the decompression:
        $bodylen = $len - $headerlen - 8;
        if ($bodylen < 1) {
            // This should never happen - IMPLEMENTATION BUG!
            return null;
        }
        $body = substr ( $data, $headerlen, $bodylen );
        $data = "";
        if ($bodylen > 0) {
            switch ($method) {
                case 8 :
                    // Currently the only supported compression method:
                    $data = gzinflate ( $body );
                    break;
                default :
                    // Unknown compression method
                    return false;
            }
        } else {
 
        // I'm not sure if zero-byte body content is allowed.
        // Allow it for now...  Do nothing...
        }
 
        // Verifiy decompressed size and CRC32:
        // NOTE: This may fail with large data sizes depending on how
        //       PHP's integer limitations affect strlen() since $isize
        //       may be negative for large sizes.
        if ($isize != strlen ( $data ) || crc32 ( $data ) != $datacrc) {
            // Bad format!  Length or CRC doesn't match!
            return false;
        }
        return $data;
    }
}
?>

使用起来非常简单,下面是一个调用程序:

<?php
/**
 * demo.php
 *
 * 客户端调用示例
 *
 * @filename demo.php
 * @version  v1.0
 * @update   2011-12-22
 * @author   homingway
 * @contact  homingway@gmail.com
 * @package  webservice
 */
 
include_once('../client/apiClient.php');
 
$server_uri = 'http://localhost/webservice/server/server.php';
 
print_r(apiClient::send($server_uri,'welcome.index'));
?>