一個PHP寫的簡單webservice服務端+客戶端

jopen 12年前發布 | 184K 次閱讀 PHP PHP開發

首先說明一下,這個小程序是我自己用PHP寫成的一個簡單的webservice系統,包括服務端的程序和客戶端的程序,無論是服務端還是客戶端在使用起來都非常的簡單方便,也可以很方便的移植到自己的項目里,我自己也已經在稍微改造后用在了自己的項目里,應用到生產環境2個多月以來都很穩定,沒有出過什么問題。

這個簡單的webservice小程序有以下幾個優點:
1. 簡單、易用,幾乎沒有什么學習成本
2. 可擴展性很強,因為簡單,所以你可以在這個基礎上擴展出很多的東西,比如返回的數據格式上可以加上xml的支持等,這個就需要自己動手了
3. 數據傳輸量小,服務端到客戶端的數據傳輸采用gzip壓縮的方式,極大的減小了數據的體積,我自己做的測試是,一份4.7M的html數據在壓縮后只有113K
4. 有一定的安全性,首先服務端和客戶端之間的通訊會有密鑰機制,同時又采取限定IP的方式保護了接口的安全。

當然,也有缺點:比如程序過于簡單,沒有對安全性和數據過過多的校驗,這個在應用到生產環境之前一定要記得加強一下;客戶端到服務端的請求默認采用get形式,傳輸的數據量有限,這個我會考慮在以后的改進中改為post,同時數據也采用gzip壓縮以后傳輸。

好了,言歸正傳,下面介紹一下代碼本身:

首先是服務端,服務端有一個主要的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; }

}</pre>然后在服務端的入口文件中調用該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();</pre>然后創建一個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';
}

}</pre>下面是客戶端的主程序: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 ), "\x1f\x8b" )) {
        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;
}

}</pre>使用起來非常簡單,下面是一個調用程序:

<?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'));</pre>轉載地址: http://hmw.iteye.com/blog/1322406

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!