文章目錄
- Section 1. 概述
- Section 2. SOAP
- Section 3. XML-RPC
來源:http://www.douzi.org/blog
學習材料是: [Wrox]Professional Open Source Web Services
Chapter 8 PHP and Web Services
英文版電子書下載[pdf]
全文分為三個部分:
- 概述。PHP進行Web Services開發的優點,在Unix系統上安裝配置PHP
- 在PHP中使用SOAP。NuSOAP工具包,NuSOAP的進階Web Service功能,如HTTP代理,SOAP over HTTPS,document style messaging。還將討論如何解決一些PHP Web Services編程將會遇到的問題,如安全問題,語言到資料類型的映射
- PHP中的XML-RPC。XML-RPC的特性,XML-RPC與SOAP的對比,然後使用Useful, Inc.實現來建立XML-RPC的用戶端和伺服器程式
下面是第一部分。
Section 1. 概述
PHP中已經通過綁定了Expat parser內建了XML支援,額外的還可以使用一些擴充程式(extension),如domxml(通過使用libxml庫提供DOM, Xpath, Xlink支援),xslt(為複雜的第三方XSLT庫如Sablotron和libxslt提供的外包程式)。
另一個對Web Service 開發有用的PHP擴充程式是CURL(Client URL Library)。CURL允許你通過不同的協議,如HTTP, HTTPS, FTP, telnet, LDAP來通訊,其中的HTTPS對Web Services與伺服器進行安全連線尤其有用。
SOAP vs XML-RPC 優缺點:
- 強大的類型擴充 (SOAP)
- 使用者自訂字元集,如US-ASCII, UTF-8, UTF-16 (SOAP)
- Specifies recipient [指定容器?] (SOAP)
- 容器遇到無法理解的報文則失敗 (SOAP)
- 便於使用 (XML-RPC)
- 設計簡單 (XML-RPC)
配置PHP:
- Apache: 為了讓PHP作為Apache的模組方式運行,使用 --with-apxs選項編譯,如 --with-apxs=/www/bin/apxs。[我現在使用的Apache2, 我編譯的PHP使用的選項是--with-apxs2=/usr/sbin/apxs]
- DOMXML: 可選功能,對解析XML文檔十分有協助。需要預先安裝好libxml庫(版本>=2.4.2),編譯時間使用 --with-dom=DIR 選項(預設DIR為/usr)
http://www.xmlsoft.org/downloads.html
libxml 2.6.4 - sources - 2.52 MB
- XSLT: 可選功能,對轉換XML資料為其他類型的文檔有協助。編譯時間使用 --enable-xslt --with-xslt-sablot 選項。必須預先安裝Sablotron XSLT庫(http://www.gingerall.com/),(預設DIR為/usr/lib或者/usr/local/lib)。
Sablotron 1.0.1 - sources - 470 kB
- CURL: 如前所述,若提供SSL支援則是必須安裝的。編譯時間使用 --with-curl=DIR 選項。也同樣需要預先安裝CURL庫(版本>=7.0.2-beta)。 [我的PHP已經安裝了。CURL Information: libcurl/7.10.7 OpenSSL/0.9.7c zlib/1.1.4]
繼續昨天的學習。今天主要是關於NuSOAP
Section 2. SOAPNuSOAP介紹:
NuSOAP是一組開源的,用來通過HTTP收發SOAP訊息的PHP類,由NuSphere Corporation (http://www.nusphere.com
) 開發。NuSOAP的一個優勢是他不是一個擴充程式,而是純粹用PHP代碼寫的,所以適用範圍比較廣。
結構:
安裝配置:
從 http://dietrich.ganx4.com/nusoap/ 下載,從zip檔案中解出nusoap.php檔案放到include目錄,在你的指令碼前面加上
include('nusoap.php');
就搞定了。
範例:
下面是一個簡單的SOAP client程式: soap_client.php 執行
//simple client
require('nusoap.php');
//要發送的變數
$myString="world";
//parameters must be passed as an array
//變數必須要轉換成數組的形式
$parameters=array($myString);
//建立一個soapclient對象,參數是server的URL
$s=new soapclient('http://www.douzi.org/me/php_ws/soap_server.php');
//調用遠程方法,傳回值存放在$result
//傳回值為PHP的變數類型,如string, integer, array
$result=$s->call('echoString', $parameters);
//錯誤偵測
if (!$err=$s->getError()) {
echo 'Result: '.$result; //success
} else {
echo 'Error: '.$err;
}
//調試,以下是SOAP請求(request)和回應(response)的報文,包括HTTP頭
echo "<xmp>".$s->request."</xmp>";
echo "<xmp>".$s->response."</xmp>";
?>
相應的server端程式: soap_server.php
//simple server
require('nusoap.php');
//建立一個新的soap_server對象,並註冊允許遠程調用的方法
$s=new soap_server;
$s->register('echoString');
$s->register('echoArray');
/*
[文章中說: 缺少了註冊這一步,任何PHP函數都將可以進行遠程調用,這將是一個極大的安全隱患。但是我嘗試過註冊是必須的。而且只有將結果return的函數才能直接聲明為遠程方法,比如echo()就不行,而strtolower()就可以。]
*/
function echoString($inputString) {
//類性檢查
if(is_string($inputString)) {
return "Hello, ".$inputString;
} else {
//soap_fault類用於產生錯誤資訊
return new soap_fault('client', '', 'The parameter to this service must be a string.');
//soap_fault(faultcode, faultactor, faultstring, faultdetail);
//上面是錯誤處理類的建構函式的格式
//faultcode 必須值。可以設定為client或server,來表明錯誤發生在哪一端。
//faultactor 在NuSOAP中尚未實現。
//faultstring 錯誤資訊。
//faultdetail 詳細錯誤資訊。你可以使用XML標記。
//除了建構函式外,soap_fault類還有一個serialize()方法
//它將錯誤資訊序列化,然後返回一個完整的SOAP報文,範例:
/*
$fault = new soap_fault('client', '', 'The inputString parameter must not be empty');
echo $fault->serialize();
*/
}
}
//示範數群組類型的使用
function echoArray($inputString) {
return $inputString[0]."+".$inputString[1];
}
//最後一步是把所有的收到的post資料都傳遞給SOAP server的service方法。它將處理請求,並調用相應的函數。
$s->service($HTTP_RAW_POST_DATA);
?>
複雜資料類型的使用:
- 數組。以下是產生的SOAP的Body部分代碼:
string1
string2
- 產生複合資料型別(compound types samples),使用soapval。以下是產生的SOAP的Body部分代碼:
123 Freezing Lane
Nome
Alaska
123451234567890
0987654321程式範例: soapval.php 執行
//soapval: general compound types samples
include('nusoap.php');$address=array(
'street'=>'123 Freezing Lane',
'city'=>'Nome',
'state'=>'Alaska',
'zip'=>12345,
'phonenumbers'=>array('home'=>'1234567890', 'mobile'=>'0987654321')
);
$s=new soapval('myAddress', 'address', $address, '', 'http://myNamespace.com');
print "<xmp>".$s->serialize()."</xmp>";
?>
WSDL
WSDL是一種用於描述Web Service的XML語言。它是一種機讀格式,把所有的訪問服務所必須的資訊提供給Web Service用戶端。NuSOAP專門提供一個類進行WDSL檔案的解析,並且從中提取資訊。soapclient對象使用wsdl類來減輕開發人員調用服務的難度。通過WSDL資訊的協助來建立報文,程式員僅僅需要知道操作的名字和參數就能調用它。
通過NuSOAP使用WSDL提供以下幾點優點:
- 所有的服務元檔案,如命名空間(namespaces),endpoint URLs,參數名(parameter names)等等都可以直接從WSDL檔案獲得,這樣就允許用戶端動態適應伺服器端的變化。因為從伺服器隨時可以獲得,所以這些資料不再需要在使用者指令碼中使用硬性編碼。
- 它允許我們使用soap_proxy類。這個類派生自soapclient類,增加了WDSL檔案中詳細列出的操作所對應的方法。現在使用者通過它可以直接調用這些方法。
- soapclient類包含一個getProxy()方法,它返回一個soap_proxy類的一個對象。soap_proxy類派生自soapclient類,增加了對應於WSDL文檔中定義的操作的方法,並且允許使用者調用一個endpoint的遠程方法。這僅僅適用於soapclient對象用WDSL檔案初始化的情況。優點是易於使用者使用,缺點是效能--PHP中建立對象是耗時的--且不為功利目的服務(and this functionality serves no utilitarian purpose)。
範例: wsdl.php 執行
//wsdl的一個簡單示範檔案
include('nusoap.php');
//SOAP源為一個提供明星生卒年月的service
//首先我們建立一個soapclient對象,把WSDL檔案的URL傳遞給建構函式,
//之後還要使用第二個參數以便使client知道我們傳遞過來的是WSDL,而不是SOAP endpoint。
$s=new soapclient('http://www.abundanttech.com/webservices/deadoralive/deadoralive.wsdl', 'wsdl');
//產生proxy類
$p=$s->getProxy();
//調用遠程函數
$sq=$p->getTodaysBirthdays();
if (!$err=$p->getError()) {
print_r($sq);
} else {
print "ERROR: $err";
}
print 'REQUEST:<xmp>'.$p->request.'</xmp>';
print 'RESPONSE:<xmp>'.str_replace('><', ">/n<", $p->response).'</xmp>';
?>
附:一個有用的Web Services的示範網站:
http://www.mindreef.net/soapscope/wsdldemo
這是最後一節,感覺PHP還是很適合做client的。這節主要是關於XML-RPC的。
我原來曾經有一篇XML-RPC學習筆記,內容和這個很類似。
Section 3. XML-RPCXML-RPC的資料類型
XML-RPC僅支援有限的幾種資料類型。下面是和PHP資料類型的的對應關係:
Useful Inc. XML-RPC實現 -- phpxmlrpc
我們使用的XML-RPC工具包是Useful, Inc.的Edd Dumbill製作的,下載網址 http://phpxmlrpc.sourceforge.net ,其中完整包括client和server的XML-RPC實現。
用戶端和伺服器端分別由 xmlrpc_client 類和 xmlrpc_server 類實現,主要用於接收和發送XML-RPC報文。xmlrpcval 類用於將PHP變數編碼為XML-RPC等價資料類型和向遠程方法傳遞參數。相反的過程使用 xmlrpc_decode() 函數。XML-RPC報文使用 xmlrpcmsg 類通過傳遞給它一個參數表來建立。
xmlrpc_client 類發送使用 xmlrpcmsg 類建立的XML-RPC報文,在伺服器端, xmlrpc_server 類解析這些收到的報文為 xmlrpcmsg 對象。然後這個對象被作為一個單獨參數傳遞給使用者函數。該函數必須返回一個 xmlrpcresp 對象, xmlrpc_server 類將其用於序列化並返回給用戶端。這個基本的體繫結構如所示。
安裝和配置
在 http://phpxmlrpc.sourceforge.net 下載,解包,然後將 xmlrpc.inc 和 xmlrpcs.inc 放置到你的包含路徑即可。
用戶端程式首部只要加入下面的包含語句:
include('xmlrpc.inc');
伺服器端程式首部要加入下面的包含語句:
include('xmlrpc.inc');
include('xmlrpcs.inc'); //伺服器端代碼
範例
XML-RPC client: xmlrpc_client.php 執行
//xmlrpc_client.php
//XML-RPC用戶端示範程式
require('xmlrpc.inc');
//建立client對象, 三個參數依次為 path, hostname, port
$s=new xmlrpc_client('/xmlrpc/xmlrpc_server.php', 'www.windix.local', 80);
//create xmlrpcval object, which allows the encoding of our variable
//建立xmlrpcval對象,將我們的PHP變數編碼為XML-RPC需要的XML形式
$inputString=new xmlrpcval('world', 'string');
//create an array of parameters
//儘管我們只有一個參數,但仍然要轉換成數組的形式,因為xmlrpcmsg的第二個參數是一個參數表
$parameters=array($inputString);
//create the message object
//建立XML-RPC報文,參數分別為 遠程方法名 和 參數表
$msg=new xmlrpcmsg('echoString', $parameters);
//send the message, get the response
//發送報文,傳回值$rsp為一個xmlrpcresp對象,它包含以下三個方法:
//faultCode() 出錯代碼,如果成功將返回0
//faultString() 出錯資訊
//value() 傳回值,以xmlrpcval對象形式存在,PHP使用前需要進行解碼
$rsp=$s->send($msg);
//check for errors
if($rsp->faultcode()==0) {
//decode the response to a PHP type
//xmlrpc_decode()函數用於將xmlrpcval對象解碼
$response=xmlrpc_decode($rsp->value());
//print results
print '
'
';
var_dump($response);
print ';
} else {
//print errors
print 'Error: '.$rsp->faultcode().', '.$rsp->faultstring().'
';
}
//show messages
//然後我們來查看一下報文內容
$msg->createpayload();
print 'REQUEST:<xmp>'.$msg->payload.'</xmp>';
print 'RESPONSE:<xmp>'.$rsp->serialize().'</xmp>';
?>
XML-RPC server: xmlrpc_server.php
//xmlrpc_server.php
//XML-RPC伺服器端示範程式
require('xmlrpc.inc');
require('xmlrpcs.inc');
//service that echoes a string
//我們用於處理遠程調用的自訂函數
//注意,該函數必須且只能有一個類型為xmlrpcmsg對象的參數
function echoString($msg) {
//decode parameters into navtive types
//首先使用xmlrpc_decode函數進行解碼
$inputString=xmlrpc_decode(array_shift($msg->params));
//check for input parameter validity
//檢查資料類型是否正確
if(is_string($inputString)) {
return new xmlrpcresp( new xmlrpcval('Hello, '.$inputString, 'string') );
} else { //or return a fault
return new xmlrpcresp(0, $xmlrpcerruser+1, "Parameter type ".gettype($inputString)." mismatched expected type.");
}
}
//instantiate the server object and register our functions
//初始化server對象,並註冊我們的函數
//下面這個數組稱為"dispatch map",其中以輸出函數名為主鍵的項又是一個數組
//包含以下三個成員:
// function 實際調用的函數名,在這裡我們的函數名也是echoString,同服務名一樣
// signature 可選。輸入和輸出參數的類型,最後一個是輸出參數,前面的都是輸入參數
// docstring 可選。包含你的服務的文檔,甚至可以有HTML內容。
//xmlrpc_server還包含第二個可選初始化參數,如果設定為0,則服務不會立刻處理請求
//需要使用server的service()方法進程才能被執行
$s=new xmlrpc_server(array(
'echoString' => array(
'function' => 'echoString',
'signature' => array( array('string','string') ),
'docstring' => 'This service echoes Hello+input stirng.'
)
));
?>