1. 论坛系统升级为Xenforo,欢迎大家测试!
    排除公告

PHP 远程过程调用 —— phprpc

本帖由 不学无术2006-01-03 发布。版面名称:后端开发

  1. 不学无术

    不学无术 Ulysses 的元神

    注册:
    2005-08-31
    帖子:
    16,714
    赞:
    39
    =====================================
    原文地址:http://www.coolcode.cn/?p=101
    =====================================

    有时候我们需要在两个 php 服务器之间执行远程过程调用,虽然用 xml-rpc 是一种解决方案,但是目前的 xml-rpc 的 php 实现用起来都非常麻烦,原来我也写过一个能够方便使用的 php 的 xml-rpc 类库, 但是那个需要安装 xmlrpc-epi 扩展,这对于不支持这个扩展的服务器就不方便了。所以我写了下面这个 phprpc 的类库,它没有使用 xml-rpc 协议,而是我自己定义的 phprpc 协议,这个虽然只能用于 php 程序之间的远程过程调用,但是使用起来比 xml-rpc 更方便。

    其他语言也不是完全不可能实现,只是实现起来比用 php 麻烦点而已,因为这里对参数和返回值的序列化与反序列化直接使用的 php 的 serialize 和 unserialize,其他语言只要能够实现这两个函数,要实现这个协议也很简单。

    下载:phprpc.php
    PHP:
    <?php
    /**
     * @author      Ma Bingyao([email protected])
     * @copyright   2005 CoolCode.CN
     * @package     PHPRPC
     * @version     0.1
     * @link        http://www.coolcode.cn/?p=101
     *
     * Example usage:
     *
     * server.php
     * <?php
     * include('phprpc.php');
     * function add($a, $b) {
     *     return $a + $b;
     * }
     * function sub($a, $b) {
     *     return $a - $b;
     * }
     * new phprpc_server(array('add', 'sub'));
     * ?>
     *
     * client.php
     * <?php
     * include('phprpc.php');
     * $rpc_client = new phprpc_client('http://test.coolcode.cn/phprpc/server.php');
     * echo $rpc_client->add(1, 2);
     * echo "<br />";
     * echo $rpc_client->Sub(1, 2); // the function name is case-insensitive
     * echo "<br />";
     * // error handle
     * echo "<pre>";
     * $result = $rpc_client->mul(1, 2);  // no mul function
     * if (get_class($result) == "phprpc_error") {
     *     print_r($result);
     * }
     * $result = $rpc_client->add(1);    // wrong arguments
     * if (get_class($result) == "phprpc_error") {
     *     print_r($result);
     * }
     * $rpc_client->use_service('wrong url');  // wrong url
     * $result = $rpc_client->add(1, 2);
     * if (get_class($result) == "phprpc_error") {
     *     print_r($result);
     * }
     * echo "</pre>";
     * ?>
     */

    class phprpc_error {
        var 
    $errno;
        var 
    $errstr;
        function 
    phprpc_error($errno$errstr) {
            
    $this->errno $errno;
            
    $this->errstr $errstr;
        }
    }

    class 
    phprpc_server {
        function 
    tolower(&$func$keys) {
            
    $func strtolower($func);
        }
        function 
    error_handler($errno$errstr) {
            echo 
    $errno;
            echo 
    "\r\n\r\n";
            echo 
    $errstr;
            exit;
        }
        function 
    phprpc_server($functions) {
            
    header("HTTP/1.1 200 OK");
            
    header("Connection: close");
            
    header("Content-Type: text/plain");
            
    header("X-Powered-By: PHPRPC Server");
            
    header("Date: " gmdate("D, d M Y H:i:s") . " GMT");
            
    error_reporting(0);
            
    set_error_handler(array(&$this'error_handler'));
            if (isset(
    $_POST['phprpc_func'])) {
                
    array_walk($functions, array(&$this'tolower'));
                
    $function strtolower(get_magic_quotes_gpc() ? stripslashes($_POST['phprpc_func']) : $_POST['phprpc_func']);
                if (
    in_array($function$functions)) {
                    if (isset(
    $_POST['phprpc_args'])) {
                        
    $arguments unserialize(base64_decode(get_magic_quotes_gpc() ? stripslashes($_POST['phprpc_args']) : $_POST['phprpc_args']));
                    }
                    else {
                        
    $arguments = array();
                    }
                    
    $result base64_encode(serialize(call_user_func_array($function$arguments)));
                }
                else {
                    
    $result "1\r\n\r\nCan't find this function $function()";
                }
            }
            else {
                
    $result "1\r\n\r\nCalled no function";
            }
            print 
    $result;
            
    restore_error_handler();
        }
    }

    class 
    phprpc_client {
        var 
    $scheme;
        var 
    $host;
        var 
    $port;
        var 
    $path;
        var 
    $user;
        var 
    $pass;
        var 
    $timeout;

        function 
    phprpc_client($url$user ''$pass ''$timeout 10) {
            
    $this->use_service($url);
            
    $this->user $user;
            
    $this->pass $pass;
            
    $this->timeout $timeout;
        }

        function 
    use_service($url) {
            
    $urlparts parse_url($url);

            if (isset(
    $urlparts['scheme']) && ($urlparts['scheme'] == "https")) {
                
    $urlparts['scheme'] = "ssl";
            }
            else {
                
    $urlparts['scheme'] = "";
            }

            if (!isset(
    $urlparts['host'])) {
                if (isset(
    $_SERVER["HTTP_HOST"])) {
                    
    $urlparts['host'] = $_SERVER["HTTP_HOST"];
                }
                else if (isset(
    $_SERVER["SERVER_NAME"])) {
                    
    $urlparts['host'] = $_SERVER["SERVER_NAME"];
                }
                else {
                    
    $urlparts['host'] = "localhost";
                }
            }

            if (!isset(
    $urlparts['port'])) {
                if (
    $urlparts['scheme'] == "ssl") {
                    
    $urlparts['port'] = 443;
                }
                else {
                    
    $urlparts['port'] = 80;
                }
            }

            if (!isset(
    $urlparts['path'])) {
                
    $urlparts['path'] = "/";
            }
            else if ((
    $urlparts['path']{0} != '/') && ($_SERVER["PHP_SELF"]{0} == '/')) {
                
    $urlparts['path'] = substr($_SERVER["PHP_SELF"], 0strrpos($_SERVER["PHP_SELF"], '/') + 1) . $urlparts['path'];
            }

            
    $this->scheme $urlparts['scheme'];
            
    $this->host $urlparts['host'];
            
    $this->port $urlparts['port'];
            
    $this->path $urlparts['path'];
        }

        function 
    __invoke($function$arguments) {
            
    $request "phprpc_func=$function";
            if (
    count($arguments) > 0) {
                
    $request .= "&phprpc_args=" base64_encode(serialize($arguments));
            }
            
    $content_len strlen($request);
            
    $errno 0;
            
    $errstr '';
            
    $host = ($this->scheme) ? $this->scheme "://" $this->host $this->host;
            
    $handle = @fsockopen($host$this->port$errno$errstr$this->timeout);
            
    $buf '';
            if (
    $handle) {
                if (
    $this->user) {
                    
    $auth "Authorization: Basic " base64_encode($this->user ":" $this->pass) . "\r\n";
                }
                
    $http_request =
                    
    "POST $this->path HTTP/1.0\r\n" .
                    
    "User-Agent: PHPRPC Client\r\n" .
                    
    "Host: $this->host:$this->port\r\n" .
                    
    $auth .
                    
    "Content-Type: application/x-www-form-urlencoded\r\n" .
                    
    "Content-Length: $content_len\r\n" .
                    
    "\r\n" .
                    
    $request;
                
    fputs($handle$http_requeststrlen($http_request));
                while (!
    feof($handle)) {
                    
    $buf .= fgets($handle128);
                }
                
    fclose($handle);
                if (
    strlen($buf)) {
                    
    $buf explode("\r\n\r\n"$buf);
                    
    $header $buf[0];
                    if (
    strpos($header'X-Powered-By: PHPRPC Server') !== FALSE) {
                        if (
    count($buf) == 2) {
                            
    $result unserialize(base64_decode($buf[1]));
                        }
                        else if (
    count($buf) == 3){
                            
    $result = new phprpc_error((int)$buf[1], $buf[2]);
                        }
                        else {
                            
    $result = new phprpc_error(E_ERROR"Unknown error");
                        }
                    }
                    else {
                        
    $result = new phprpc_error(E_ERROR"Wrong PHPRPC Server");
                    }
                }
                else {
                    
    $result = new phprpc_error(E_ERROR"No data received from server");
                }
            }
            else {
                
    $result = new phprpc_error($errno$errstr);
            }
            return 
    $result;
        }

        function 
    __call($function$arguments, &$return) {
            
    $return $this->__invoke($function$arguments);
            if (
    phpversion() < 5) return true;
        }

        function 
    call($function$args) {
            
    $arguments func_get_args();
            
    array_shift($arguments);
            return 
    $this->__invoke($function$arguments);
        }
    }

    if (
    function_exists("overload") && version_compare(phpversion(), "5""<")) {
        
    overload('phprpc_client');
    }
    ?>