在 PHP 中本身带有一个用 C 语言实现的 xmlrpc 扩展,叫 xmlrpc-epi。因为它是用 C 语言实现的,所以从速度上来说比用 PHP 实现的那些 xmlrpc 扩展要快的多。但是在实际应用中却发现很少有人用这个扩展,大都是用 PHP 脚本实现的 xmlrpc 的扩展。原因可能有以下两点:一是这个扩展需要在服务器上打开,如果没有服务器操作权限,使用这个扩展就不现实了。第二个原因就是这个扩展在 PHP 手册中的说明太少了,而且提供的函数都很基础,要使用的话就很麻烦了。针对第二个问题,我对这个扩展进行了封装,将它封装成了3个 类:xmlrpc_error、xmlrpc_client 和 xmlrpc_server。当然最主要的是后两个类,即 xmlrpc_client 和 xmlrpc_server。这两个类大大简化了创建 xmlrpc 客户端和服务器的步骤。
2006年1月11日 更新
修正了某些非标准端口的服务器上指定相对路径进行调用的错误。
2006年1月6日 更新
增加了对 PHP5 的支持。
大家可以通过下面的实例来看一下现在创建 xmlrpc 服务器和客户端的步骤有多么简单。
服务器端代码
下载: server.php
require_once('class_xmlrpc.php');
下载: server.php
require_once('class_xmlrpc.php');
function Add($method, $params) {
return $params[0] + $params[1];
}
function Sub($method, $params) {
return $params[0] - $params[1];
}
function Mul($method, $params) {
return $params[0] * $params[1];
}
function Div($method, $params) {
return $params[0] / $params[1];
}
return $params[0] + $params[1];
}
function Sub($method, $params) {
return $params[0] - $params[1];
}
function Mul($method, $params) {
return $params[0] * $params[1];
}
function Div($method, $params) {
return $params[0] / $params[1];
}
$xmlrpc_server = new xmlrpc_server();
$xmlrpc_server->register_method("Math.add", "Add");
$xmlrpc_server->register_method("Math.sub", "Sub");
$xmlrpc_server->register_method("Math.Mul", "Mul");
$xmlrpc_server->register_method("Math.Div", "Div");
$xmlrpc_server->call_method();
?>
客户端代码
下载: client.php
require_once('class_xmlrpc.php');
$xmlrpc_server->register_method("Math.add", "Add");
$xmlrpc_server->register_method("Math.sub", "Sub");
$xmlrpc_server->register_method("Math.Mul", "Mul");
$xmlrpc_server->register_method("Math.Div", "Div");
$xmlrpc_server->call_method();
?>
客户端代码
下载: client.php
require_once('class_xmlrpc.php');
$xmlrpc_client = new xmlrpc_client('server.php', 'Math');
$a = 100;
$b = 20;
echo "/$a = $a; /$b = $b
";
echo '$a + $b = ' . $xmlrpc_client->add($a, $b) . '
';
echo '$a - $b = ' . $xmlrpc_client->sub($a, $b) . '
';
echo '$a * $b = ' . $xmlrpc_client->call('Mul', $a, $b) . '
';
echo '$a / $b = ' . $xmlrpc_client->invoke('Math.Div', $a, $b) . '
';
?>
虽然上面的代码很简单,但是还是有两点需要注意的地方的。
$a = 100;
$b = 20;
echo "/$a = $a; /$b = $b
";
echo '$a + $b = ' . $xmlrpc_client->add($a, $b) . '
';
echo '$a - $b = ' . $xmlrpc_client->sub($a, $b) . '
';
echo '$a * $b = ' . $xmlrpc_client->call('Mul', $a, $b) . '
';
echo '$a / $b = ' . $xmlrpc_client->invoke('Math.Div', $a, $b) . '
';
?>
虽然上面的代码很简单,但是还是有两点需要注意的地方的。
第一,xmlrpc 的方法是支持名空间(namespace)的,为了简化调用——省略方法前面的名空间,我们在初始化 $xmlrpc_client 时,给出了一个名空间的参数“Math”,这样下面通过方法名直接调用或者通过call来调用方法时,就可以省略名空间前缀了。如果想要改变名空间,只要 给 $xmlrpc_client->namespace 赋值就可以了。如果只是临时改变,也可以通过 invoke 方法来用全名(即带有名空间的方法名)来调用。
第二,xmlrpc 和 PHP5 的方法是区分大小写的,而 PHP4 的方法是不区分大小写的,而且在 PHP4 中,所有的函数或方法名都是储存为小写,因此不论是 add 也好,Sub 也好,最后它们所调用的方法都是小写的。所以 PHP5 中可以直接调用定义中有大写字母的方法,而 PHP4 中却不能。也就是说,如果想要在 PHP4 中直接通过方法名来访问 xmlrpc 方法的话,那么必须保证 xmlrpc 的方法名在定义时是小写的,否则就会产生找不到相应方法的错误。如果要在 PHP4 中调用在定义时就有大写字母的 xmlrpc 方法该怎么办呢?其实很简单,用 call 方法调用就可以了,第一个参数就是要调用的方法名的字符串,这个字符串是可以区分大小写的。也可以用 invoke 方法来调用,不同的地方就是如果有名空间的话,需要明确的写出来。
当然,上面的代码之所以可以那么简单,主要是因为它们包含了这个文件:
下载: class_xmlrpc.php
/**
* @author 马秉尧
* @copyright (C) 2005 CoolCode.CN
* @package xmlrpc-epi-php
* @version 0.7
*/
/**
* @author 马秉尧
* @copyright (C) 2005 CoolCode.CN
* @package xmlrpc-epi-php
* @version 0.7
*/
class xmlrpc_error {
var $faultCode;
var $faultString;
function xmlrpc_error($code, $string) {
$this->faultCode = $code;
$this->faultString = $string;
}
}
var $faultCode;
var $faultString;
function xmlrpc_error($code, $string) {
$this->faultCode = $code;
$this->faultString = $string;
}
}
class xmlrpc_server {
var $server;
function xmlrpc_server() {
$this->server = xmlrpc_server_create();
register_shutdown_function(array(&$this, "__xmlrpc_server"));
}
var $server;
function xmlrpc_server() {
$this->server = xmlrpc_server_create();
register_shutdown_function(array(&$this, "__xmlrpc_server"));
}
function register_method($method_name, $function) {
xmlrpc_server_register_method($this->server, $method_name, $function);
}
xmlrpc_server_register_method($this->server, $method_name, $function);
}
function xmlrpc_server_add_introspection_data($desc) {
xmlrpc_server_add_introspection_data($this->server, $desc);
}
xmlrpc_server_add_introspection_data($this->server, $desc);
}
function register_introspection_callback($function) {
xmlrpc_server_register_introspection_callback($this->server, $function);
}
xmlrpc_server_register_introspection_callback($this->server, $function);
}
function call_method($user_data = null) {
if (isset($GLOBALS['HTTP_RAW_POST_DATA'])) {
$request = $GLOBALS['HTTP_RAW_POST_DATA'];
}
else {
$request = '';
}
$output_options = array(
"output_type" => "xml",
"verbosity" => "pretty",
"escaping" => array("markup"),
"version" => "xmlrpc",
"encoding" => "utf-8"
);
$response = xmlrpc_server_call_method($this->server, $request, $user_data, $output_options);
header("HTTP/1.1 200 OK");
header("Connection: close");
header("Content-Length: " . strlen($response));
header("Content-Type: text/xml; charset=utf-8");
header("Date: " . gmdate("D, d M Y H:i:s") . " GMT");
print $response;
}
if (isset($GLOBALS['HTTP_RAW_POST_DATA'])) {
$request = $GLOBALS['HTTP_RAW_POST_DATA'];
}
else {
$request = '';
}
$output_options = array(
"output_type" => "xml",
"verbosity" => "pretty",
"escaping" => array("markup"),
"version" => "xmlrpc",
"encoding" => "utf-8"
);
$response = xmlrpc_server_call_method($this->server, $request, $user_data, $output_options);
header("HTTP/1.1 200 OK");
header("Connection: close");
header("Content-Length: " . strlen($response));
header("Content-Type: text/xml; charset=utf-8");
header("Date: " . gmdate("D, d M Y H:i:s") . " GMT");
print $response;
}
function __xmlrpc_server() {
xmlrpc_server_destroy($this->server);
}
}
xmlrpc_server_destroy($this->server);
}
}
class __xmlrpc_client {
var $scheme;
var $host;
var $port;
var $path;
var $user;
var $pass;
var $namespace;
var $timeout;
var $scheme;
var $host;
var $port;
var $path;
var $user;
var $pass;
var $namespace;
var $timeout;
function __xmlrpc_client($url, $namespace = '', $user = '', $pass = '', $timeout = 10) {
$this->use_service($url);
$this->namespace = $namespace;
$this->user = $user;
$this->pass = $pass;
$this->timeout = $timeout;
}
$this->use_service($url);
$this->namespace = $namespace;
$this->user = $user;
$this->pass = $pass;
$this->timeout = $timeout;
}
function use_service($url) {
$urlparts = parse_url($url);
$urlparts = parse_url($url);
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['scheme'])) {
if (!isset($_SERVER["HTTPS"]) ||
$_SERVER["HTTPS"] == "off" ||
$_SERVER["HTTPS"] == "") {
$urlparts['scheme'] = "";
}
else {
$urlparts['scheme'] = "https";
}
}
if (!isset($urlparts['port'])) {
$urlparts['port'] = $_SERVER["SERVER_PORT"];
}
}
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['scheme'])) {
if (!isset($_SERVER["HTTPS"]) ||
$_SERVER["HTTPS"] == "off" ||
$_SERVER["HTTPS"] == "") {
$urlparts['scheme'] = "";
}
else {
$urlparts['scheme'] = "https";
}
}
if (!isset($urlparts['port'])) {
$urlparts['port'] = $_SERVER["SERVER_PORT"];
}
}
if (isset($urlparts['scheme']) && ($urlparts['scheme'] == "https")) {
$urlparts['scheme'] = "ssl";
}
else {
$urlparts['scheme'] = "";
}
$urlparts['scheme'] = "ssl";
}
else {
$urlparts['scheme'] = "";
}
if (!isset($urlparts['port'])) {
if ($urlparts['scheme'] == "ssl") {
$urlparts['port'] = 443;
}
else {
$urlparts['port'] = 80;
}
}
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"], 0, strrpos($_SERVER["PHP_SELF"], '/') + 1) . $urlparts['path'];
}
$urlparts['path'] = "/";
}
else if (($urlparts['path']{0} != '/') && ($_SERVER["PHP_SELF"]{0} == '/')) {
$urlparts['path'] = substr($_SERVER["PHP_SELF"], 0, strrpos($_SERVER["PHP_SELF"], '/') + 1) . $urlparts['path'];
}
$this->scheme = $urlparts['scheme'];
$this->host = $urlparts['host'];
$this->port = $urlparts['port'];
$this->path = $urlparts['path'];
}
$this->host = $urlparts['host'];
$this->port = $urlparts['port'];
$this->path = $urlparts['path'];
}
function __invoke($function, $arguments) {
$output = array(
"output_type" => "xml",
"verbosity" => "pretty",
"escaping" => array("markup"),
"version" => "xmlrpc",
"encoding" => "utf-8");
$request = xmlrpc_encode_request($function, $arguments, $output);
$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) {
$auth = '';
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: xmlrpc-epi-php/0.6 (PHP)/r/n" .
"Host: $this->host:$this->port/r/n" .
$auth .
"Content-Type: text/xml; charset=utf-8/r/n" .
"Content-Length: $content_len/r/n" .
"/r/n" .
$request;
fputs($handle, $http_request, strlen($http_request));
while (!feof($handle)) {
$buf .= fgets($handle, 128);
}
fclose($handle);
if (strlen($buf)) {
$xml = substr($buf, strpos($buf, " if (strlen($xml)) {
$result = xmlrpc_decode($xml);
}
else {
$result = new xmlrpc_error(6, "No data received from server");
}
}
else {
$result = new xmlrpc_error(6, "No data received from server");
}
}
else {
$result = new xmlrpc_error(5, "Didn't receive 200 OK from remote server");
}
return $result;
}
$output = array(
"output_type" => "xml",
"verbosity" => "pretty",
"escaping" => array("markup"),
"version" => "xmlrpc",
"encoding" => "utf-8");
$request = xmlrpc_encode_request($function, $arguments, $output);
$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) {
$auth = '';
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: xmlrpc-epi-php/0.6 (PHP)/r/n" .
"Host: $this->host:$this->port/r/n" .
$auth .
"Content-Type: text/xml; charset=utf-8/r/n" .
"Content-Length: $content_len/r/n" .
"/r/n" .
$request;
fputs($handle, $http_request, strlen($http_request));
while (!feof($handle)) {
$buf .= fgets($handle, 128);
}
fclose($handle);
if (strlen($buf)) {
$xml = substr($buf, strpos($buf, " if (strlen($xml)) {
$result = xmlrpc_decode($xml);
}
else {
$result = new xmlrpc_error(6, "No data received from server");
}
}
else {
$result = new xmlrpc_error(6, "No data received from server");
}
}
else {
$result = new xmlrpc_error(5, "Didn't receive 200 OK from remote server");
}
return $result;
}
function invoke($function, $args) {
$arguments = func_get_args();
array_shift($arguments);
return $this->__invoke($function, $arguments);
}
$arguments = func_get_args();
array_shift($arguments);
return $this->__invoke($function, $arguments);
}
function call($function, $args) {
$function = ($this->namespace == '') ? $function : $this->namespace . '.' . $function;
$arguments = func_get_args();
array_shift($arguments);
return $this->__invoke($function, $arguments);
}
}
$function = ($this->namespace == '') ? $function : $this->namespace . '.' . $function;
$arguments = func_get_args();
array_shift($arguments);
return $this->__invoke($function, $arguments);
}
}
if (function_exists("overload") && version_compare(phpversion(), "5", "<")) {
require_once('php4_xmlrpc_client.php');
}
else {
require_once('php5_xmlrpc_client.php');
}
?>
下载: php4_xmlrpc_client.php
class xmlrpc_client extends __xmlrpc_client {
function __call($function, $arguments, &$return) {
$function = ($this->namespace == '') ? $function : $this->namespace . '.' . $function;
$return = $this->__invoke($function, $arguments);
return true;
}
}
overload('xmlrpc_client');
?>
下载: php5_xmlrpc_client.php
class xmlrpc_client extends __xmlrpc_client {
function __call($function, $arguments) {
$function = ($this->namespace == '') ? $function : $this->namespace . '.' . $function;
return $this->__invoke($function, $arguments);
}
}
?>
好了,有了上面这个文件,以后再用 php 作 xmlrpc 程序就易如反掌了。哈哈哈哈~~~~
require_once('php4_xmlrpc_client.php');
}
else {
require_once('php5_xmlrpc_client.php');
}
?>
下载: php4_xmlrpc_client.php
class xmlrpc_client extends __xmlrpc_client {
function __call($function, $arguments, &$return) {
$function = ($this->namespace == '') ? $function : $this->namespace . '.' . $function;
$return = $this->__invoke($function, $arguments);
return true;
}
}
overload('xmlrpc_client');
?>
下载: php5_xmlrpc_client.php
class xmlrpc_client extends __xmlrpc_client {
function __call($function, $arguments) {
$function = ($this->namespace == '') ? $function : $this->namespace . '.' . $function;
return $this->__invoke($function, $arguments);
}
}
?>
好了,有了上面这个文件,以后再用 php 作 xmlrpc 程序就易如反掌了。哈哈哈哈~~~~
当然这两个类还包含其它一些功能,比如客户端调用时可以使用 http 基本认证,可以设置连接服务器的超时时间(默认10秒),服务器端你还可以给各个方法添加自我描述的文档。这些我就不举例子了。
关于xmlrpc-epi请参考:http://xmlrpc-epi.sourceforge.net
关于xmlrpc请参考:http://www.xmlrpc.org
沒有留言:
張貼留言