### 在 PHP 中利用 XML 技术构造远程服务(2)

四、基于XML_RPC的Web服务
利用XML_RPC构造和使用服务是很方便的。企业为自己提供的各种服务部署XML_RPC服务器，用户、客户软件和客户企业就可以使用这种服务构造出高端服务或者面向最终用户的应用。这种提供更有效、廉价和优质服务的竞争将极大地提高应用服务的质量。
但这里还存在一些问题有待解决，例如怎样编目、索引、搜索Web上的服务？UDDI试图解决这个问题，不过这个标准并不简单，而且业界对它的反应也尚未明了。然而，在企业内部应用XML_RPC不仅能够改善代码的可重用性，而且还会带来一种全新的分布式计算模式，在此后的数年中它必将成为一种重要的知识财富。XML_RPC的发展从解决分布式计算问题以及成为服务式Web的基本层面开始，从而获得了一个非常好的开端，其后必将紧随着人们对该标准的热衷。既然如此，现在就让我们来看看XML_RPC的实际应用吧！

4.1 在PHP中应用XML_RPC

对于提供Web服务来说，PHP是一种非常理想的语言。我们只需编写好PHP代码然而把它放到一个合适的位置，就立即有了一个可通过URL“调用”的服务。PHP中的XML_RPC实现可能复杂也可能简单，但我们拥有许多种选择。这里我们选用的是来自Useful Information Company的XML_RPC实现，它的代码和文档可以从http://xmlrpc.usefulinc.com/下载。
这个XML_RPC实现的基本类涉及两个文件：
xmlrpc.inc：包含XML_RPC的php客户端所需要的类
xmlrpcs.inc：包含XML_RPC的php服务器所需要的类

4.2 客户端

编写XML_RPC客户端意味着：
1.创建一个XML_RPC请求消息
2.设置XML_RPC参数
3.创建一个XML_RPC消息
4.发送消息
5.获得应答
6.解释应答
请看下面这个例子：

＜?php $f=new xmlrpcmsg('examples.getStateName',array(new xmlrpcval(14, "int"))); $c=new xmlrpc_client("/RPC2", "betty.userland.com", 80); $r=$c->send($f); $v=$r->value();
if (!$r->faultCode()) {
print "状态代码". $HTTP_POST_VARS["stateno"] . "是" .
$v->scalarval() . "＜BR＞";
print "＜HR＞这是服务器的应答＜BR＞＜PRE＞" .
htmlentities($r->serialize()). "＜/PRE＞＜HR＞\n";
} else {
print "错误: ";
print "代码: " . $r->faultCode() .
" 原因: '" .$r->faultString()."'＜BR＞";
}
?＞

在这个例子中，我们先创建了一个调用“examples.getStateName”方法的XML_RPC消息，并传递了一个类型为“int”值为14的整数参数。然后，我们创建了一个描述待调用URL（路径、域和端口）的客户。接着，我们发送了消息，接收应答对象并检查错误。如果不存在错误，我们就显示结果。
编写RPC客户程序时要用到的主要函数如下：
创建客户用：

$client=new xmlrpc_client($server_path, $server_hostname, $server_port);

发送消息的方法是：

$response=$client->send($xmlrpc_message);

它返回的是xmlrpcresp的一个实例。我们所传递的消息是xmlrpcmsg的实例，它用如下方法创建：

$msg=new xmlrpcmsg($methodName, $parameterArray);

methodName是待调用的方法（过程）的名字，parameterArray是xmlrpcval对象的php数组。例如：

$msg=new xmlrpcmsg("examples.getStateName", array(new xmlrpcval(23, "int")));

xmlrpcval对象可以用如下形式创建：

＜?php $myVal=new xmlrpcval($stringVal); $myVal=new xmlrpcval($scalarVal, "int" | "boolean" | "string" | "double" | "dateTime.iso8601" | "base64"); $myVal=new xmlrpcval($arrayVal, "array" | "struct"); ?＞

第一种形式创建的是xmlrpc字符串值。第二种形式创建的是描述值和类型的值。第三种形式通过在数组之类的结构中组合其他xmlrpc值创建复杂的对象，例如：

＜?php $myArray=new xmlrpcval(array(new xmlrpcval("Tom"), new xmlrpcval("Dick"),new xmlrpcval("Harry")), "array"); $myStruct=new xmlrpcval(array( "name" => new xmlrpcval("Tom"), "age" => new xmlrpcval(34, "int"),
"geek" => new xmlrpcval(1, "boolean")),"struct");
?＞

应答对象是xmlrpcresp类型，通过调用客户对象的send方法获得。在服务器端，我们可以通过如下方式创建xmlrpcresp类型的对象：

$resp=new xmlrpcresp($xmlrpcval);

而在客户端，则使用如下方法从应答获取xmlrpcval：

$xmlrpcVal=$resp->value();

接下来我们就可以用下面这种方式获取描述应答结果的PHP变量：

$scalarVal=$val->scalarval();

对于复杂的数据类型，有两个函数非常有用，这两个函数都在xmlrpc.inc内：

$arr=xmlrpc_decode($xmlrpc_val);

该函数返回一个PHP数组，其中包含了xmlrpcval变量$xmlrpc_val之内的数据，这些数据已经被转换成PHP本身具有的变量类型。

$xmlrpc_val=xmlrpc_encode($phpval);

该函数返回一个xmlrpcval类型的值，其中包含了$phpval描述的PHP数据。对于数组和结构，此方法能够进行递归分析。注意，这里不存在对非基本数据类型（如base-64数据，或者日期-时间数据）的支持。

4.3 服务器端

利用xmlrpcs.inc提供的类编写服务非常简单。要创建一个服务，我们按照如下方式创建xmlrpc_server的实例：

＜?php $s=new xmlrpc_server( array("examples.myFunc" => array("function" => "foo"))); ?＞

传递给xmlrpc_server构造函数的是一个联合数组的联合数组。过程“examples.myFunc”调用“foo”函数，由于这个原因foo被称为方法句柄。
编写方法句柄很简单。下面是一个方法句柄的骨架：

＜?php function foo ($params) { global $xmlrpcerruser; // 引入用户错误代码值 // $params是一个xmlrpcval对象的数组 if ($err) {
// 错误条件
return new xmlrpcresp(0, $xmlrpcerruser+1, // 用户错误1
"Error!");
} else {
// 成功
return new xmlrpcresp(new xmlrpcval("Fine!", "string"));
}
}
?＞

可以看到，程序检查了错误，如存在错误则返回错误（从$xmlrpcerruser+1开始）；否则如果一切正常，则返回描述操作成功信息的xmlrpcresp。
五、应用实例
在下面这个例子中我们将构造一个服务。对于给定的数值n，服务返回n*2。客户端利用该服务计算5*2的值。
服务器端的代码如下：

＜?php include("xmlrpc.inc"); include("xmlrpcs.inc"); function foo ($params) {
global $xmlrpcerruser; // 引入用户错误代码值
// $params是xmlrpcval对象的一个数组
$vala=$params->params[0];
$sval=$vala->scalarval();
$ret=$sval*2;
return new xmlrpcresp(new xmlrpcval($ret, "int"));
}
$s=new xmlrpc_server( array("product" =>
array("function" => "foo")));
?＞

客户端代码如下：

＜?php include("xmlrpc.inc"); if ($HTTP_POST_VARS["number"]!="") { $f=new xmlrpcmsg('product',array(new xmlrpcval($HTTP_POST_VARS["number"], "int"))); $c=new xmlrpc_client("/xmlrpc/servfoo.php", "luigi.melpomenia.com.ar", 80);
$c->setDebug(0);
$r=$c->send($f);
$v=$r->value();
if (!$r->faultCode()) {
print "Number ". $HTTP_POST_VARS["number"] . " is " .
$v->scalarval() . "＜BR＞";
print "＜HR＞来自服务器的结果!＜BR＞＜PRE＞" .
htmlentities($r->serialize()). "＜/PRE＞＜HR＞\n";
} else {
print "操作失败: ";
print "代码: " . $r->faultCode() .
" 原因: '" .$r->faultString()."'＜BR＞";
}
}
print "＜FORM METHOD=\"POST\"＞
＜INPUT NAME=\"number\" VALUE=\"${number}\"＞
＜input type=\"submit\" value=\"go\" name=\"submit\"＞＜/FORM＞＜P＞
输入一个数值";
?＞