标签归档:web服务

PHP函数参考 – Web服务 – SOAP

PHP的Web服务SOAP扩展需要libxml的支持,跟http://blog.ifeeline.com/1157.html这里描述的一样。这里不再重复。

PHP的SOAP扩展支持SOAP1.1和SOAP1.2,它们都可以以WSDL和NO WSDL方式运行。

要使用PHP的SOAP扩展,需要在编译PHP时需要加入–enable-soap参数。由于SOAP会向客户端传递一个描述文件(使用wsdl时),可以把描述文件缓存起来,相关的配置(以下是默认值):

soap.wsdl_cache_enabled	1	
soap.wsdl_cache_dir	/tmp	
soap.wsdl_cache_ttl	86400	
soap.wsdl_cache		1	
soap.wsdl_cache_limit	5

在测试时最好把soap.wsdl_cache_enabled设置为0。

PHP中的SOAP可分两部分,一部分是作为客户端是消费,一部分是作为服务端提供服务。本文主要探讨客户端:

$webservice_url = "http://******/services/order?wsdl";
$userToken = '******';

$client = new SoapClient($webservice_url);

print_r($client->__getFunctions());
print_r($client->__getTypes());

这里拿了个现成的例子测试,看看都有哪些函数和类型:

/usr/local/php-5.5.15/bin/php soap.php 
Array
(
    [0] => deleteOrderResponse deleteOrder(string $userToken, long $orderId)
    [1] => auditOrderResponse auditOrder(string $userToken, long $orderId)
    [2] => createOrderResponse createOrder(string $userToken, createOrderRequest $createOrderRequest)
    [3] => createOrderResponse createAndAuditOrder(string $userToken, createOrderRequest $createOrderRequest)
    [4] => getTransportWayListResponse getTransportWayList(string $userToken)
)
Array(......)

这里看到服务端提供了哪些API以及如何调用这些API,还有这些API要发送的数据结构和返回的数据结构也有清晰定义。这个是如何做到的?

实际上,__getFunctions()和__getTypes()函数只是解析从服务端返回的XML文件(WSDL)而已。服务端的程序需要构建这个XML文档并定义对应的可调用的方法。参看下图:

在构建SoapClient对象时就可以传递服务地址(wsdl),也可以调用SoapClient::__setLocation指定;通过调用SoapClient::__setCookie设置随SOAP请求一起发送的cookie;通过调用SoapClient::__setSoapHeaders设置SOAP头部信息,注意这个不是请求的头部信息,是Soap的头部,在发起请求时,实际传递一个XML字符串,PHP SOAP扩展提供了一个专门的SoapHeader类,可以自定义一些信息作为XML字符串的一部分发送:

$client = new SoapClient($webservice_url,array('trace' => 1));
$header = new SoapHeader('http://soapinterop.org/echoheader/', 
                            'echoMeStringRequest',
                            'hello world');
$client->__setSoapHeaders($header);

echo $client->__getLastRequest();

这个会输出:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://service.hop.service.ws.hlt.com/" xmlns:ns2="http://soapinterop.org/echoheader/"><SOAP-ENV:Header><ns2:echoMeStringRequest>hello world</ns2:echoMeStringRequest></SOAP-ENV:Header><SOAP-ENV:Body><ns1:getTransportWayList><userToken>2a924b2173d845dab94054223a110339</userToken></ns1:getTransportWayList></SOAP-ENV:Body></SOAP-ENV:Envelope>

注意看这里的SOAP-ENV:Header标签,跟SOAP-ENV:Body并列。具体需要参看SoapHeader类。

继续看如下例子:

$webservice_url = "http://******/services/order?wsdl";
$userToken = '******';

$client = new SoapClient($webservice_url,array('trace' => 1));
$something = $client->getTransportWayList($userToken);

echo "\n----\n".$client->__getLastRequestHeaders();
echo "\n----\n".$client->__getLastRequest();
echo "\n----\n".$client->__getLastResponseHeaders();
echo "\n----\n".$client->__getLastResponse();

这里的四个函数主要是查看请求头部和请求内容 和 响应头部和响应内容,需要注意的是如果要使用这四个方法有效,必须在建立SoapClient对象时,它的第二参数的数组有trace为true,比如array((‘trace’ => 1))。

输出:

----
POST /xms/services/order HTTP/1.1
Host: ****:8086
Connection: Keep-Alive
User-Agent: PHP-SOAP/5.5.15
Content-Type: text/xml; charset=utf-8
SOAPAction: ""
Content-Length: 326


----
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://service.hop.service.ws.hlt.com/"><SOAP-ENV:Body><ns1:getTransportWayList><userToken>*****5dab9******</userToken></ns1:getTransportWayList></SOAP-ENV:Body></SOAP-ENV:Envelope>

----
HTTP/1.1 200 OK
Server: nginx/1.4.4
Date: Mon, 11 Aug 2014 10:41:55 GMT
Content-Type: text/xml;charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Set-Cookie: JSESSIONID=6AF7237052D518B02B37D45CF9F84120; Path=/xms/; HttpOnly

----
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns1:getTransportWayListResponse xmlns:ns1="http://service.hop.service.ws.hlt.com/"><return><success>true</success>......

头部输出没有什么特别的,就是HTTP的请求和响应头,不过要注意Content-Type,它是text/xml。请求体是一个XML字符串,里面包含了需要调用的方法和方法的参数。响应体也是一个XML,里面包含了响应的数据。实际我们在调用方法时,SOAP扩展会把请求转换成XML请求体,而服务端收到后把XML进行解析,然后调用对应方法,而返回的XML数据在客户端收到后也进行了对应的转换。

下面谈一下具体的方法调用。

以上代码有:

$client->getTransportWayList($userToken);

实际上,SoapClient()类并没有getTransportWayList()方法,这个方法是来自服务端的,通过调用SoapClient::__getFunctions方法可以看到这个方法,这里这个方法的调用告诉我们,通过wsdl文件暴露的方法,都可以通过SoapClient类对象直接调用,这个调用内部会转换成一个发送XML的HTTP请求。getTransportWayList()这类来自服务端的方法也可以通过调用SoapClient::__soapCall来间接调用。

SoapClient最后需要了解的方法是SoapClient::__doRequest,自己的类可以继承SoapClient然后重写这个方法,那么就可以实现自定义发送请求的操作,默认是自动的(调用方法就开始)。

客户端在调用方法时如果发生了错误该如何捕获?PHP SOAP提供了一个is_soap_fault()的方法,方法参数是调用的结果。需要首先知道的是,SOAP的调用如果发生错误,那么它将抛出一个SoapFault对象的异常,但是如果在创建SoapClient对象时如果第二参数的数组指定了exception为false,比如array(‘exceptions’ => 0),那么当SOAP的调用发生错误时直接返回SoapFault对象,它封装了相关的错误信息。以下官方的例子:

<?php
$client = new SoapClient("some.wsdl", array('exceptions' => 0));
$result = $client->SomeFunction();
if (is_soap_fault($result)) {
    trigger_error("SOAP Fault: (faultcode: {$result->faultcode}, faultstring: {$result->faultstring})", E_USER_ERROR);
}
?>

#################################
<?php
try {
    $client = new SoapClient("some.wsdl");
    $result = $client->SomeFunction(/* ... */);
} catch (SoapFault $fault) {
    trigger_error("SOAP Fault: (faultcode: {$fault->faultcode}, faultstring: {$fault->faultstring})", E_USER_ERROR);
}
?>

SOAP的服务器端部分如果使用WSDL,主要是这个文件的编写,鉴于PHP习惯用来作为SOAP的客户端,所以这里不探讨PHP SOAP的服务端的内容了,附上一篇之前写的文章作为参考:PHP Web服务 SOAP 快速入门

原创文章,转载务必保留出处。
永久链接:http://blog.ifeeline.com/1159.html

PHP函数参考 – Web服务 – XML-RPC

PHP函数参考 – Web服务 – XML-RPC

从PHP4开始,XML-RPC就已经可用了,现在是PHP5.6都要到达稳定版本了,但是这个XML-RPC扩展的函数还声称是实验性的,实在令人不解。

实际上,要在PHP中实现XML-RPC,不一定要使用PHP自带的这个XML-RPC扩展。XML-RPC是构建在HTTP之上的,数据传递是依靠XML,所以使用PHP来自己实现也是没有问题的,而也的确存在这样的第三方库,不过这里想探讨的还是PHP自带的这个XML-RPC扩展。

由于需要解析XML,所以PHP的libxml扩展是必须的(默认就是启用的,除非明确使用disable-libxml),PHP的libxml扩展实际是libxml2库的包装器,所以需要确保安装了:

rpm -qa | grep xml
libxml2-2.7.6-14.el6_5.2.x86_64
libxml2-devel-2.7.6-14.el6_5.2.x86_64

在编译安装PHP时一般使用–with-libxml-dir=/usr来明确指定libxml安装的地方。

要在PHP中使用XML-RPC,还要在编译时添加–with-xmlrpc。

首先简单说明一下工作过程:客户端使用POST方法把一个XML文档提交过来,这个文档包含了要调用的方法和参数,然后服务端获取这个XML文档并解析出对应的方法和参数,然后调用对应的方法,把结果返回给客户端。对于客户端,只要能构建POST用的XML(当然要按照XML-RPC的XML构建规则)就可以,而没有限制需要使用什么语言。客户端的XML作为POST请求体的一部分,一般可以使用$GLOBALS[‘HTTP_RAW_POST_DATA’]获取原始的POST数据。

例子:

###xmlrpc_server.php
<?php
function xmlrpc_test_func($method, $params) {
        $prms = '';
        if(is_array($params[0])){
                $params = $params[0];
        }
        foreach($params as $pi=>$pv){
                $prms .= $pi."=>".$pv."--";
        }
        $prms = rtrim($prms,'--');
        $return = "Call the server method is $method, and params are:$prms\n";
        return $return;
}

$xmlrpc = xmlrpc_server_create();

xmlrpc_server_register_method($xmlrpc, "xmlrpc_test", "xmlrpc_test_func");

$request = $GLOBALS['HTTP_RAW_POST_DATA'];

$xmlrpc_response = xmlrpc_server_call_method($xmlrpc, $request, null);

header("Content-Type: text/xml");
echo $xmlrpc_response;

xmlrpc_server_destroy($xmlrpc);


###xmlrpc_client.php
<?php
function rpc_use_socket_call($host, $port, $rpc_server, $request) {
        $fp = fsockopen($host, $port);

        $query = "POST $rpc_server HTTP/1.0\nUser_Agent: XML-RPC Client\nHost: ".$host."\nContent-Type: text/xml\nContent-Length: ".strlen($request)."\n\n".$request."\n";

        if (!fputs($fp, $query, strlen($query)))
        {
                $errstr = "Write error";
                return false;
        }

        $contents = "";
        while (!feof($fp)){
                $contents .= fgets($fp);
        }
        fclose($fp);
        return $contents;
}

$host  = "nginx.vfeelit.com";
$port  = 80;
$rpc_server = "/xmlrpc_server.php";

$request = xmlrpc_encode_request("xmlrpc_test", array("test","data"=>"post data","key"=>"121256"));

$response = rpc_use_socket_call($host, $port, $rpc_server, $request);

print_r($response);

xmlrpc_server_create()建立XML-RPC服务端句柄,xmlrpc_server_register_method()注册供客户端调用的方法,第一参数是XML-RPC服务端句柄,第二参数是客户端端调用的方法名字,第三参数是客户端调用方法的映射方法,就是实际将在服务端被执行的方法。xmlrpc_server_call_method()第一参数是XML-RPC服务端句柄,第二参数来自客户端的XML字符串,第三个参数是自定义数据。这个方法会自动解码来自客户端的XML字符串,然后调用这个XML中指定的方法,在这里客户端调用的方法是xmlrpc_test,实际被影射到xmlrpc_test_func,这个方法稍微有点特殊,它的第一个参数是客户端实际调用的方法名字,第二个参数是来自客户端的参数,把方法的执行结果以XML结构的组织返回。

客户端中使用xmlrpc_encode_request()把要调用的方法和参数传递进去,然后这个方法把这些数据装换成一个XML字符串,通过SOCKET的方法,POST过去,不使用xmlrpc_encode_request()方法也可以,不过要按照要求组装XML。当然,不用SOCKET也是可以的,比如在浏览器端用JS也可以一样POST数据。

输出结果:

HTTP/1.1 200 OK
Server: nginx/1.6.0
Date: Sun, 03 Aug 2014 05:29:16 GMT
Content-Type: text/html
Connection: close
X-Powered-By: PHP/5.5.15

<?xml version="1.0" encoding="iso-8859-1"?>
<methodResponse>
<params>
 <param>
  <value>
   <string>Call the server method is xmlrpc_test, and params are:0=&#62;test--data=&#62;post data--key=&#62;121256&#10;</string>
  </value>
 </param>
</params>
</methodResponse>

这里看到,响应头被输出了。接下来是响应的内容,它是XML结构的字符串。所以客户端获取这个XML之后还需要解析这个XML获取数据。从整个流程下来看XML-RPC的使用实际是非常简单的。

在客户端代码中的:

$request = xmlrpc_encode_request("xmlrpc_test", array("test","data"=>"post data","key"=>"121256"));
//输出$request

直接输出:

<?xml version="1.0" encoding="iso-8859-1"?>
<methodCall>
<methodName>rpc_server</methodName>
<params>
 <param>
  <value>
   <struct>
    <member>
     <name>test</name>
     <value>
      <string>123</string>
     </value>
    </member>
    <member>
     <name>getdata</name>
     <value>
      <string>base64data</string>
     </value>
    </member>
   </struct>
  </value>
 </param>
</params>
</methodCall>

这个就是要发送的XML,如果自己组装,也要按照这样的规则,服务端也是按照同样的规则解析这个XML的。

对于轻量级别Web服务开发,PHP的XML-RPC还是简单实用的。不过由于它还是实验性的,版本升级上有一些风险,看起来使用SOAP可能更好一点。不过目前比较流行OAuth,这些PHP中都提供支持(OAuth需要安装PECL包)。

原创文章,转载务必保留出处。
永久链接:http://blog.ifeeline.com/1157.html

PHP Web服务 SOAP 快速入门

PHP中的SOAP扩展支持SOAP1.1和SOAP1.2和WSDL 1.1。

这个扩展需要需要libxml,表示需要使用enable-libxml来启用,不过默认是开启的。如果要开启SOAP的支持,需要使用enable-soap选项来编译PHP。

SOAP配置选项:

soap.wsdl_cache_enabled	        1	是否开启WSDL缓存
soap.wsdl_cache_dir		/tmp	WSDL缓存目录
soap.wsdl_cache_ttl		86400	WSDL缓存过期时间
soap.wsdl_cache			1	WSDL缓存类型 1表示磁盘 2表示内存
soap.wsdl_cache_limit		5	在内存缓存的文件最大数

SoapClient类
SoapClient类提供了一个使用了SOAP 1.1、SOAP1.2服务器的客户端。可以使用WSDL和non-WSDL模式。

实例化一个SoapClient:

$soap = new SoapClient(null,array('location'=>'http://localhost/soap/Server.php','uri'=>'server_namespace'));

要发送SOAP请求,必须先实例化一个新的SOAPClient对象,并向构造函数传递相应Web服务的WSDL地址。WSDL(Web Services Description Language, Web服务描述语言)是一个XML词汇表,它可以让实现者创建一个定义它的Web服务都支持哪些方法和变量的文件。这个文件会放在Web服务器上供其他程序调用。

当让SOAP扩展指向一个WSDL文件时,这个扩展会自动地为相应的Web服务创建一个对象,而你可以像操作PHP类一样来操作这个对象。这个对象甚至能够知道每个方法都带什么参数,以及每个参数的类型。之所以这一点很重要,是因为SOAP与PHP不同,它是严格类型的。WSDL允许SOAP扩展把PHP变量强制转换成适当的类型。

如果需要向一个没有提供WSDL文件的服务器发送一个SOAP请求,必须自己指定必要的信息。像WSDL文件参数的位置传递一个null值,而主要的服务设置(如位置和名称空间URI)则以一个选项数组提供。

入门例子:

// 定义一个类 Calculate.php
class Calculate{
	 public function getResult($base = ''){
		
		for($i  = 1; $i<=100; $i++){
			$r += $i;
		}
		return (int)$base+$r;
	}
}

// 实现服务端 Server.php
require_once("Calculate.php");

$s = new SoapServer(null, array('uri'=>'Server_Namespace'));

$s->setClass("Calculate");
$s->handle();

// 客户端 Client.php
try{
	$soap = new SoapClient(null,array('location'=>'http://localhost/soap/Server.php','uri'=>'Server_Namespace'));
	
	echo $soap->getResult();
	echo "<br />";
	echo $soap->__soapCall('getResult',array());
	
	echo "<br /><br />";

	echo $soap->getResult(100);
	echo "<br />";
	echo $soap->__soapCall('getResult',array(100));

}catch(SoapFault $e){
	echo $e->getMessage();
}catch(Exception $e){
	echo $e->getMessage();	
}

输出:

5050
5050

5150
5150 

更多内容可以参考:http://www.php.net/manual/zh/book.soap.php

永久链接:http://blog.ifeeline.com/554.html
原创文章,转载务必保留出处。