在PHP中利用XML技術構造遠程服務(轉)_PHP
最後更新:2016-06-01
來源:互聯網
上載者:User
關鍵字
遠程
服務
構造
技術
利用
XML_RPC
在PHP中利用XML技術構造遠程服務
未來的Web將是以服務為中心的Web,XML_RPC標準使得編寫和應用服務變得非常簡單。本文介紹XML_RPC標準及其PHP實現,並通過執行個體示範了如何在PHP中開發XML_RPC服務和客戶程式。
一、服務式Web
從內容供應商所採用的簡單方法到UDDI(Universal Description,Discovery and Integration)的未來構想,業界已經有大量關於“服務式Web”的說明和評論。就Web的初創階段來說,它只是一個文檔的集散地,提供的只是一些可瀏覽的資訊。隨著Web的發展,在Web上運行服務越來越具有吸引力。未來,Web將成為企業為客戶和其他企業提供便捷服務的載體。B2B和B2C模式間的協同就可以看成是一種服務式Web。
一個很重要的問題是,Web上究竟可以提供哪些服務?Web能夠提供的服務非常多,其中有些服務現在已經在使用,有些服務在不久的將來就會出現。為了說明問題,下面列出了一小部分可以通過Web提供的服務:
面向主題的垂直搜尋引擎。
供使用者尋找資訊的知識庫。
使用者可以請教問題的專家系統。
銀行服務。
新聞和資訊出版服務。
數字化支付相關的服務。
圖形處理服務。
衛生和健康服務。
那麼,企業和組織通過Web提供服務的正確途徑是什麼呢?這是一個很重要的問題。今天,有些服務提供HTML介面,它們通過文檔的形式提供服務,但在服務介面的背後隱藏著什嗎?在佔領Web的競賽中,Web瀏覽器並不孤單,行動電話、手持功能以及微波爐之類的裝置都想要訪問Web、查詢資料庫、轉換資料、提取資訊,等等。要實現真正的服務式Web,在表現層(HTML)之下應該還有另外一層。
二、XML_RPC標準
XML或許是近10年來最為重要的標準,XML詞彙表(Vocabulary)為企業構造服務環境提供了基石。要構建服務式Web就有必要學習XML_RPC標準,這不僅是因為XML_RPC對於把服務放到Web上很有用,而且因為XML_RPC是一種已經成形的、很容易採用的標準。對於B2B服務來說,提供服務的標準是極其重要的,共同遵循標準的公司可以利用其它公司提供的服務獲得快速的增長。無法想象在各種私人的服務標準之上可以建立起真正的服務式Web,服務必須有一種可以遵循的標準。
XML_RPC是一種面向Internet分散式處理的標準。RPC即為Remote Procedure Call(遠端程序呼叫)的縮寫,它是一種遠程調用機制,用於調用可能駐留在其他機器之上以及可能用其他語言編寫的過程。遠端程序呼叫是分散式運算的重要支柱。例如,在一個分散式運算環境中,我們可以尋找和利用在其他機器上啟動並執行執行加法和減法操作的過程,執行加法操作的過程可能用APL編寫、在RS6000機器上運行,執行減法操作的過程可能用C編寫、在Unix上運行。其他要使用這種分散式運算器的開發人員同樣可以利用它們,或者他也可以選用另外更好的計算機。
在RPC中,過程(Procedure)是最主要的構件,伺服器提供的就是供用戶端調用的過程。過程可以接收參數並返回結果。XML_RPC以HTTP作為協議載體,通過發送和接收資料的XML詞彙表實現RPC機制。XML_RPC伺服器接收XML_RPC請求並返回XML_RPC應答,XML_RPC客戶程式發送XML_RPC請求並接收XML_RPC應答。伺服器和客戶必須按照XML_RPC標準的要求處理應答和請求。
三、XML_RPC協議
完整的XML_RPC規範可以在http://www.xmlrpc.com/spec找到。下面是其要點說明。
3.1 XML_RPC請求
XML_RPC請求應該是HTTP POST請求,它的本文是XML格式。請求的XML部分格式如下:
<?xml version="1.0" ?>
<methodCall>
<methodName>examples.getStateName</methodName>
<params>
<param>
<value><i4>41</i4></value>
</param>
</params>
</methodCall>
指定資料發送到哪裡的URL並未在這裡指定。如果伺服器專門用來進行RPC處理,它可能是“/”。上述XML文檔中的有效載荷是一個“methodCall”結構。methodCall必須包含一個“methodName”子項目,“methodName”子項目包含一個描述待調用方法的字串。如何解釋“methodName”的內容完全由伺服器決定,例如它可以是一個執行檔案的名字,可以是資料庫中記錄的名字,或者任何其他東西。如果過程接收參數,“methodCall”可以包含一個“params”元素以及若干個“param”子項目。每一個“param”元素包含一個帶有類型描述元的值,類型描述元如下表所示:
標記 說明
<i4>或<int> 四位元組的帶正負號的整數,如12
<boolean> 0(false),或1(true)
<string> 字串,如“Hello World”
<double> 雙精確度帶符號浮點數,如-12.214
<dateTime.iso8601> 日期/時間,如19980717T14:08:55
<base64> base64編碼的位元據,如eW91IGbid0IHJlQgdGhpcyE
3.1.1 結構
值可以是一個結構,結構用元素描述。每個包含多個,每個包含一個和一個。下面是一個由兩個元素構成的結構:
<struct>
<member>
<name>name</name>
<value><string>member1</string></value>
</member>
<member>
<name>member2</name>
<value><i4>19</i4></value>
</member>
</struct>
可以嵌套,任意可以包含或者任意其它類型,包括。
3.1.2 數組
值可以是數群組類型,數組用元素描述。每個元素包含一個元素,元素裡面可以包含任意多個元素。下面是數組元素的一個例子:
<array>
<data>
<value><boolean>0</boolean></value>
<value><i4>9</i4></value>
<value><string>Hello</string></value>
</data>
</array>
元素沒有名字。如前例所示,元素的值可以是各種類型。元素可以嵌套,任何都可以包含或者其他類型,如上面介紹的。
3.2 XML_RPC應答
XML_RPC應答是一個HTTP應答,內容類型是text/xml。應答本文的格式如下:
<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value><string>ABCDEFG</string></value>
</param>
</params>
</methodResponse>
可能包含一個結構,或者可能包含一個結構,具體由程序呼叫是否成功決定。結構與XML請求中的一樣,元素的文法如下:
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>4</int></value>
</member>
<member>
<name>faultString</name>
<value><string>Error!</string></value>
</member>
</struct>
</value>
</fault>
四、基於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資料。對於數組和結構,此方法能夠進行遞迴分析。注意,這裡不存在對非基礎資料型別 (Elementary Data Type)(如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>
輸入一個數值";
?>
結束語:XML_RPC服務的運作還涉及其他許多基礎設施和基礎工作,如分布式過程的編目和索引機制,又如在程式設計語言中處理XML_RPC的更好介面等。有關XML_RPC和服務式Web的報道非常多,讓我們密切關注它們的發展吧!