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

来源:互联网 发布:手机域名的价值 编辑:程序博客网 时间:2024/06/05 08:22

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


首先说明一下,这个小程序是我自己用PHP写成的一个简单的webservice系统,包括服务端的程序和客户端的程序,无论是服务端还是客户端在使用起来都非常的简单方便,也可以很方便的移植到自己的项目里,我自己也已经在稍微改造后用在了自己的项目里,应用到生产环境2个多月以来都很稳定,没有出过什么问题。 

这个简单的webservice小程序有以下几个优点: 
1. 简单、易用,几乎没有什么学习成本 
2. 可扩展性很强,因为简单,所以你可以在这个基础上扩展出很多的东西,比如返回的数据格式上可以加上xml的支持等,这个就需要自己动手了 
3. 数据传输量小,服务端到客户端的数据传输采用gzip压缩的方式,极大的减小了数据的体积,我自己做的测试是,一份4.7M的html数据在压缩后只有113K 
4. 有一定的安全性,首先服务端和客户端之间的通讯会有密钥机制,同时又采取限定IP的方式保护了接口的安全。 

当然,也有缺点:比如程序过于简单,没有对安全性和数据过过多的校验,这个在应用到生产环境之前一定要记得加强一下;客户端到服务端的请求默认采用get形式,传输的数据量有限,这个我会考虑在以后的改进中改为post,同时数据也采用gzip压缩以后传输。 

好了,言归正传,下面介绍一下代码本身: 

首先是服务端,服务端有一个主要的class组成:apiServer.php 
Php代码  收藏代码
  1. <?php  
  2. /** 
  3.  * apiServer.php 
  4.  * 
  5.  * webservice主类 
  6.  * 
  7.  * @filename apiServer.php 
  8.  * @version  v1.0 
  9.  * @update   2011-12-22 
  10.  * @author   homingway 
  11.  * @contact  homingway@gmail.com 
  12.  * @package  webservice 
  13.  */  
  14. define('API_AUTH_KEY',  'i8XsJb$fJ!87FblnW');  
  15. class apiServer{  
  16.   
  17.     //请求参数  
  18.     public $request = array();  
  19.   
  20.     //是否ip限制  
  21.     public $ip_limit = true;  
  22.     //允许访问的IP列表  
  23.     public $ip_allow = array('127.0.0.1','192.168.0.99');  
  24.   
  25.     public $default_method = 'welcome.index';  
  26.     public $service_method = array();  
  27.   
  28.     //私有静态单例变量  
  29.     private static $_instance = null;  
  30.   
  31.     /** 
  32.      * 构造方法,处理请求参数 
  33.      */  
  34.     private function __construct(){  
  35.         $this->dealRequest();  
  36.     }  
  37.   
  38.     /** 
  39.      * 单例运行 
  40.      */  
  41.     public static function getInstance(){  
  42.         if(self::$_instance === null){  
  43.             self::$_instance = new self();  
  44.         }  
  45.         return self::$_instance;  
  46.     }  
  47.   
  48.     /** 
  49.      * 运行 
  50.      */  
  51.     public function run(){  
  52.         //授权  
  53.         if(!$this->checkAuth()){  
  54.             exit('3|Access Denied');  
  55.         }  
  56.         $this->getApiMethod();  
  57.         include_once(API_SERVICE_PATH.'/'.$this->service_method['service'].'.php');  
  58.         $serviceObject = new $this->service_method['service'];  
  59.         if($this->request['param']){  
  60.             $result = call_user_func_array(array($serviceObject,$this->service_method['method']),$this->request['param']);  
  61.         } else {  
  62.             $result = call_user_func(array($serviceObject,$this->service_method['method']));  
  63.         }  
  64.         if(is_array($result)){  
  65.             $result = json_encode($result);  
  66.         }  
  67.         $result = gzencode($result);  
  68.         exit($result);  
  69.     }  
  70.   
  71.     /** 
  72.      * 检查授权 
  73.      */  
  74.     public function checkAuth(){  
  75.         //检查参数是否为空  
  76.         if(!$this->request['time'] || !$this->request['method']   || !$this->request['auth']){  
  77.             return false;  
  78.         }  
  79.   
  80.         //检查auth是否正确  
  81.         $server_auth = md5(md5($this->request['time'].'|'.$this->request['method'].'|'.API_AUTH_KEY));  
  82.         if($server_auth != $this->request['auth']){  
  83.             return false;  
  84.         }  
  85.   
  86.         //ip限制  
  87.         if($this->ip_limit){  
  88.             $remote_ip = $this->getIP();  
  89.             $intersect = array_intersect($remote_ip,$this->ip_allow);  
  90.             if(emptyempty($intersect)){  
  91.                 return false;  
  92.             }  
  93.         }  
  94.   
  95.         return true;  
  96.     }  
  97.   
  98.     /** 
  99.      * 获取服务名和方法名 
  100.      */  
  101.     public function getApiMethod(){  
  102.         if(strpos($this->request['method'], '.') === false){  
  103.             $method = $this->default_method;  
  104.         } else {  
  105.             $method = $this->request['method'];  
  106.         }  
  107.         $tmp = explode('.'$method);  
  108.         $this->service_method = array('service'=>$tmp[0],'method'=>$tmp[1]);  
  109.         return $this->service_method;  
  110.     }  
  111.   
  112.     /** 
  113.      * 获取和处理请求参数 
  114.      */  
  115.     public function dealRequest(){  
  116.         $this->request['time'] = $this->_request('time');  
  117.         $this->request['method'] = $this->_request('method');  
  118.         $this->request['param'] = $this->_request('param');  
  119.         $this->request['auth'] = $this->_request('auth');  
  120.         if($this->request['param']){  
  121.             $this->request['param'] = json_decode(urldecode($this->request['param']),true);  
  122.         }  
  123.     }  
  124.   
  125.     /** 
  126.      * 获取request变量 
  127.      * @param string $item 
  128.      */  
  129.     private function _request($item){  
  130.         return isset($_REQUEST[$item]) ? trim($_REQUEST[$item]) : '';  
  131.     }  
  132.   
  133.     /** 
  134.      * 设置IP限制 
  135.      * @param bool $limit 
  136.      */  
  137.     public function setIPLimit($limit=true){  
  138.         $this->ip_limit = $limit;  
  139.     }  
  140.   
  141.     /** 
  142.      * 获取客户端ip地址 
  143.      */  
  144.     public function getIP(){  
  145.         $ip = array();  
  146.         if(isset($_SERVER['REMOTE_ADDR'])){  
  147.             $ip[] = $_SERVER['REMOTE_ADDR'];  
  148.         }  
  149.         if(isset($_SERVER['HTTP_VIA'])){  
  150.             $tmp = explode(', ',$_SERVER['HTTP_X_FORWARDED_FOR']);  
  151.             $ip = array_merge($ip,$tmp);  
  152.         }  
  153.         $ip = array_unique($ip);  
  154.         return $ip;  
  155.     }  
  156.   
  157. }  


然后在服务端的入口文件中调用该class,并启动服务即可,如: 
Php代码  收藏代码
  1. <?php  
  2. /** 
  3.  * server.php 
  4.  * 
  5.  * 自定义数据接口的入口 
  6.  * 
  7.  * @filename server.php 
  8.  * @version  v1.0 
  9.  * @update   2011-12-22 
  10.  * @author   homingway 
  11.  * @contact  homingway@gmail.com 
  12.  * @package  webservice 
  13.  */  
  14.   
  15. //API的根目录  
  16. define('API_PATH',dirname(__FILE__));  
  17.   
  18. //服务目录  
  19. define('API_SERVICE_PATH',API_PATH.'/service');  
  20. define('API_LIB_PATH',  API_PATH.'/lib');  
  21.   
  22. //服务核心class  
  23. include_once(API_LIB_PATH.'/apiServer.php');  
  24.   
  25. //运行  
  26. apiServer::getInstance()->run();  


然后创建一个service的目录,里面就是自己的接口class,如welcome.php: 
Php代码  收藏代码
  1. <?php  
  2. /** 
  3.  * welcome.php 
  4.  * 
  5.  * 功能代码 
  6.  * 
  7.  * @filename welcome.php 
  8.  * @version  v1.0 
  9.  * @update   2011-12-22 
  10.  * @author   homingway 
  11.  * @contact  homingway@gmail.com 
  12.  * @package  webservice 
  13.  */  
  14.   
  15. class welcome{  
  16.   
  17.     public function index(){  
  18.         return 'hello service';  
  19.     }  
  20.   
  21. }  



下面是客户端的主程序:apiClient.php 
Php代码  收藏代码
  1. <?php  
  2. /** 
  3.  * apiClient.php 
  4.  * 
  5.  * webservice客户端程序 
  6.  * 
  7.  * @filename apiClient.php 
  8.  * @version  v1.0 
  9.  * @update   2011-12-22 
  10.  * @author   homingway 
  11.  * @contact  homingway@gmail.com 
  12.  * @package  webservice 
  13.  */  
  14.   
  15. define('API_AUTH_KEY',  'i8XsJb$fJ!87FblnW');  
  16.   
  17. class apiClient{  
  18.   
  19.     public static function send($url,$method,$param=array()){  
  20.         $time = time();  
  21.         $auth = md5(md5($time.'|'.$method.'|'.API_AUTH_KEY));  
  22.         if(!is_array($param) || emptyempty($param)){  
  23.             $json_param = '';  
  24.         } else {  
  25.             $json_param = urlencode(json_encode($param));  
  26.         }  
  27.         $api_url = $url.'?method='.$method.'&time='.$time.'&auth='.$auth.'&param='.$json_param;  
  28.         $content = file_get_contents($api_url);  
  29.         if(function_exists('gzdecode')){  
  30.             $content = gzdecode($content);  
  31.         } else {  
  32.             $content = self::gzdecode($content);  
  33.         }  
  34.         return $content;  
  35.     }  
  36.   
  37.     public static function gzdecode($data) {  
  38.         $len = strlen ( $data );  
  39.         if ($len < 18 || strcmp ( substr ( $data, 0, 2 ), "\x1f\x8b" )) {  
  40.             return null; // Not GZIP format (See RFC 1952)  
  41.         }  
  42.         $method = ord ( substr ( $data, 2, 1 ) ); // Compression method  
  43.         $flags = ord ( substr ( $data, 3, 1 ) ); // Flags  
  44.         if ($flags & 31 != $flags) {  
  45.             // Reserved bits are set -- NOT ALLOWED by RFC 1952  
  46.             return null;  
  47.         }  
  48.         // NOTE: $mtime may be negative (PHP integer limitations)  
  49.         $mtime = unpack ( "V"substr ( $data, 4, 4 ) );  
  50.         $mtime = $mtime [1];  
  51.         $xfl = substr ( $data, 8, 1 );  
  52.         $os = substr ( $data, 8, 1 );  
  53.         $headerlen = 10;  
  54.         $extralen = 0;  
  55.         $extra = "";  
  56.         if ($flags & 4) {  
  57.             // 2-byte length prefixed EXTRA data in header  
  58.             if ($len - $headerlen - 2 < 8) {  
  59.                 return false; // Invalid format  
  60.             }  
  61.             $extralen = unpack ( "v"substr ( $data, 8, 2 ) );  
  62.             $extralen = $extralen [1];  
  63.             if ($len - $headerlen - 2 - $extralen < 8) {  
  64.                 return false; // Invalid format  
  65.             }  
  66.             $extra = substr ( $data, 10, $extralen );  
  67.             $headerlen += 2 + $extralen;  
  68.         }  
  69.         $filenamelen = 0;  
  70.         $filename = "";  
  71.         if ($flags & 8) {  
  72.             // C-style string file NAME data in header  
  73.             if ($len - $headerlen - 1 < 8) {  
  74.                 return false; // Invalid format  
  75.             }  
  76.             $filenamelen = strpos ( substr ( $data, 8 + $extralen ), chr ( 0 ) );  
  77.             if ($filenamelen === false || $len - $headerlen - $filenamelen - 1 < 8) {  
  78.                 return false; // Invalid format  
  79.             }  
  80.             $filename = substr ( $data$headerlen$filenamelen );  
  81.             $headerlen += $filenamelen + 1;  
  82.         }  
  83.   
  84.         $commentlen = 0;  
  85.         $comment = "";  
  86.         if ($flags & 16) {  
  87.             // C-style string COMMENT data in header  
  88.             if ($len - $headerlen - 1 < 8) {  
  89.                 return false; // Invalid format  
  90.             }  
  91.             $commentlen = strpos ( substr ( $data, 8 + $extralen + $filenamelen ), chr ( 0 ) );  
  92.             if ($commentlen === false || $len - $headerlen - $commentlen - 1 < 8) {  
  93.                 return false; // Invalid header format  
  94.             }  
  95.             $comment = substr ( $data$headerlen$commentlen );  
  96.             $headerlen += $commentlen + 1;  
  97.         }  
  98.   
  99.         $headercrc = "";  
  100.         if ($flags & 1) {  
  101.             // 2-bytes (lowest order) of CRC32 on header present  
  102.             if ($len - $headerlen - 2 < 8) {  
  103.                 return false; // Invalid format  
  104.             }  
  105.             $calccrc = crc32 ( substr ( $data, 0, $headerlen ) ) & 0xffff;  
  106.             $headercrc = unpack ( "v"substr ( $data$headerlen, 2 ) );  
  107.             $headercrc = $headercrc [1];  
  108.             if ($headercrc != $calccrc) {  
  109.                 return false; // Bad header CRC  
  110.             }  
  111.             $headerlen += 2;  
  112.         }  
  113.   
  114.         // GZIP FOOTER - These be negative due to PHP's limitations  
  115.         $datacrc = unpack ( "V"substr ( $data, - 8, 4 ) );  
  116.         $datacrc = $datacrc [1];  
  117.         $isize = unpack ( "V"substr ( $data, - 4 ) );  
  118.         $isize = $isize [1];  
  119.   
  120.         // Perform the decompression:  
  121.         $bodylen = $len - $headerlen - 8;  
  122.         if ($bodylen < 1) {  
  123.             // This should never happen - IMPLEMENTATION BUG!  
  124.             return null;  
  125.         }  
  126.         $body = substr ( $data$headerlen$bodylen );  
  127.         $data = "";  
  128.         if ($bodylen > 0) {  
  129.             switch ($method) {  
  130.                 case 8 :  
  131.                     // Currently the only supported compression method:  
  132.                     $data = gzinflate ( $body );  
  133.                     break;  
  134.                 default :  
  135.                     // Unknown compression method  
  136.                     return false;  
  137.             }  
  138.         } else {  
  139.   
  140.         // I'm not sure if zero-byte body content is allowed.  
  141.         // Allow it for now...  Do nothing...  
  142.         }  
  143.   
  144.         // Verifiy decompressed size and CRC32:  
  145.         // NOTE: This may fail with large data sizes depending on how  
  146.         //       PHP's integer limitations affect strlen() since $isize  
  147.         //       may be negative for large sizes.  
  148.         if ($isize != strlen ( $data ) || crc32 ( $data ) != $datacrc) {  
  149.             // Bad format!  Length or CRC doesn't match!  
  150.             return false;  
  151.         }  
  152.         return $data;  
  153.     }  
  154. }  


使用起来非常简单,下面是一个调用程序: 
Php代码  收藏代码
  1. <?php  
  2. /** 
  3.  * demo.php 
  4.  * 
  5.  * 客户端调用示例 
  6.  * 
  7.  * @filename demo.php 
  8.  * @version  v1.0 
  9.  * @update   2011-12-22 
  10.  * @author   homingway 
  11.  * @contact  homingway@gmail.com 
  12.  * @package  webservice 
  13.  */  
  14.   
  15. include_once('../client/apiClient.php');  
  16.   
  17. $server_uri = 'http://localhost/webservice/server/server.php';  
  18.   
  19. print_r(apiClient::send($server_uri,'welcome.index'));  
0 0
原创粉丝点击