您的位置:首页 > 编程语言 > PHP开发

SOAP通信详解实例[PHP]

2015-12-15 09:31 701 查看
php soap编码转换
在给CHINAZ资讯(dedecms)做同步bbsmaxpassport登陆api时,因为bbsmax使用utf-8编码,而资讯这边用的是GBK编码,导致乱码。开始想自己转码,但有点麻烦。后面想SOAP既然用来针对不同平台,那肯定也包括编码问题,就又认真看了PHP手册,介绍里虽然没有提到,却发现Examples里有,访问很简单。
$client = new SoapClient("some.wsdl",array('encoding'=〉'GBK')); 
只要这么简单,剩下的PHP自己帮忙实现了!
通过SoapHeader实现身份认证
之前一直抱怨php的soap很傻,在client端有设置header的方法,在server端却没有取header的方法。那是很傻很天真,直接用正则表达式从soap信封的header中提取header信息。
最近由于有项目要发布webservice,重新燃起对soap的兴趣,看了w3的英文文档,那是个云里雾里。收集了一些资料,做了一个关于saopheader进行身份认证的实验。
在这个实验中,假定soap client用一个字符串作为身份认证的标识,soapserver取到这个字符串后,对其进行辨认,如果与期望相符合,认证通过,如果不符,抛出soapFault。
理论就不多说了,我也不懂,直接上代码
client.php
〈?php
$cli = new SoapClient(null, array('uri' =〉'http://127.0.0.1/namespace/', 'location' =〉'http://localhost/server.php', 'trace' =〉 true));
$h = new SoapHeader('http://127.0.0.1/namespace/', 'auth','123456789', false, SOAP_ACTOR_NEXT);
$cli-〉__setSoapHeaders(array($h));
try {
echo $cli-〉say();
} catch (Exception $e) {
echo $e-〉getMessage();
}

server.php
〈?php
class Server{
public function auth($a)
{
if($a != '123456789'){
throw new SoapFault('Server', '您无权访问');
}
}
function say()
{
return 'Hi';
}

$srv = new SoapServer(null, array('uri' =〉'http://localhost/namespace'));
$srv-〉setClass('Server');
$srv-〉handle();

以上代码就实现了认证的功能,最关键的地方就是SoapHeader的构造。soapHeader有五个构造参数,
Namespace 无用
Name 鉴别身份标识的函数或者方法名
Data 存放标识身份的字符串
mustUnderstand 是否必须处理该header 
actor 处理该header的角色(不是太理解)
注意看红色的一行,构造了一个soapHeader,header的名称为"auth",data为"123456789",mustUnderstand为false,actor为SOAP_ACTOR_NEXT。
注意观察server.php中的server类有一个方法"auth",刚好与header的名称对应,方法auth的参数$u,就是soapHeader的data,soapServer接收到这个请求会,先调用auth方法,并把"123456789"作为参数传递给该方法。
mustUnderstand参数为false时,即便没有auth这个方法,say方法也会被调用,但是如果它为true的话,如果auth方法不存在,就会返回一个Soapfault告知该header没有被处理。
actor参数指名那些role必须处理该header,这儿我理解得不是太透彻,不好说。
大概就这样,关键点在于SoapHeader的构造。
soap官方:http://www.w3.org/TR/soap12-part1/
[转]使用SoapHeader实现Soap请求验证
在PHP的Soap Extension中, 对于SoapServer来说,并没有方法可用得到/处理客户端发送的SoapHeader信息.
网上也有很多人认为, 只能通过读取POST过来的请求XML文件, 分析, 才能得到客户端发送过来的SoapHeader.但,其实在SoapServer端, 其实是有一种办法, 可用把SoapHeader当作一个请求来处理,从而获取到客户端提交的SoapHeader信息. 
假设客户端代码如下: 

〈?php
\\*
* 保存用户名和密码的载体
*/
class SoapUserInfo {
\\**
* @var char $name
*/
public $name;
\\**
* @var char $password
*/
public $password;
public function __construct($l, $p) {
$this-〉Password = $p;
$this-〉Username = $l;
}
}
?〉
然后客户端生成SoapHeader 
〈?php
$soap_header = new SoapHeader("http://www.laruence.com",'Authorise'
, new SoapUserInfo('laruence', 'password'), false,SOAP_ACTOR_NEXT);
?〉
也许细心的同学会注意到第4个参数FALSE和第5个参数SOAP_ACTOR_NEXT, 这是什么呢?我最后再讲. 
然后, 创建客户端, 绑定SoapHeader 
〈?php
$client = new SoapClient($wsdl);
$client-〉__setSoapHeaders(array($soap_header));
$client-〉__soapCall('request', array());
?〉
现在, 客户端已经发起了请求, 请求中也包含了SoapHeader,其中有了我们验证需要的用户名/密码信息. 
那么, 在服务端, 该如何做呢? 
〈?php
$server = new SoapServer('laruence.wsdl');
$server-〉setClass('InterfaceClass');
$server-〉handle();
?〉
关键的地方就在, 服务端接收请求以后, 会实例化一个处理类, 然后分析SoapHeader,接着就会调用InterfaceClass::Authorise这个方法(Authorise是我们请求头中的变量名), 所以,我们就可用在InterfaceClass类中, 定义个Authorise方法,并在这个方法中对SoapHeader中的信息做验证. 
然后, 请求体(Soap body)中的方法被调用, 因为不论Authorise方法返回什么(除非exit),请求体中的方法一定会被调用, 所以要寻找个变量记录验证的结果. 
〈?php
class InterfaceClass {
\\**
* @var bool $authorized
*/
private $authorized = FALSE;
\\*
* Authentication function
*
* @param string username
* @param string password
*/
public function Authentication($username, $password) {
$this-〉authorized = validateUser($username, $password);
}
\\*
* Test method
*/
public function request(){
if ($this-〉authorized) {
//验证成功, 继续处理.
} else {
//验证失败, 拒绝请求.
}
}
}
?〉
当然, 对于网上说的另外一种方法, 通过分析请求的XML文件, 也可以: 
〈?php
class InterfaceClass {
\\**
* @var bool $authorized
*/
private $authorized = FALSE;
function __construct() {
$xml = file_get_contents('php://input');
//分析xml, 获得SoapHeader数据, 验证
}
}
?〉
Must Understand 
这个参数指明了, 是否服务端必须要了解SoapHeader, 如果这个参数为真, 而服务端并不能识别响应的Header,则会引发一个Soap Fault(Header not understood). 
SOAP_ACTOR_NEXT 
actor指明了SoapHeader要传递给谁, 被谁处理. 
SOAP_ACTOR_NEXT的意思就是, 下一个接受到这个请求头的Service,在本文的例子中只有一个Server,当然也就没有关系了. 
在SoapServer的构造函数中, 我们可以指明一个Server的Actor,比如: 
〈?php
$server = new SoapServer($wsdl, array('actor' =〉'laruence'));
?〉
这样, 我们就可以在Client的SoapHeader中, 通过设置actor是laruence,来让指定的Server来获得我们设置的头部的信息. 
php SOAP实现Web 服务例子php 先要开启php_soap模块
一。
方法1
服务器端文件叫 server.php
〈?php
$soap = newSoapServer(null,array('uri'=〉"http://10.10.10.24/"));//输入本台服务器的ip地址
$soap-〉addFunction('say'); //添加输出函数
$soap-〉addFunction(SOAP_FUNCTIONS_ALL); //不要忘了这个
$soap-〉handle(); //注意
function say($sth){
return $sth;

?〉 
客户端 输出的是hello world
〈?php
try {
$client = new SoapClient(null,
array('location' =〉"http://10.10.10.24/server.php",'uri'=〉"http://10.10.10.24/")
);
echo $client-〉say("hello world");
} catch (SoapFault $fault){
echo "Error: ",$fault-〉faultcode,", string:",$fault-〉faultstring;
}
?〉 
二。
服务器端文件server.php:
〈?php
$classmap = array();
//注意和实例一的不同
$soap = newSoapServer(null,array('uri'=〉"http://10.10.10.24/", "classmap" =〉$classmap));
$soap-〉setClass('Myclass');
$soap-〉handle();
class Myclass {
function say($someword){
return $someword;
}
}
?〉
客户端 输出的是xyz world 
〈?
try {
$client = new SoapClient(null,
array('location' =〉"http://www.xiao688.com/server.php",'uri'=〉"http://www.xiao688.com/")
);
var_dump($client);
echo $client-〉say("xyz world");
} catch (SoapFault $fault){
echo "Error: ",$fault-〉faultcode,", string:",$fault-〉faultstring;


php soap实例
php提供了一个专门用于soap操作的扩展库,使用该扩展库后可以直接在php中进行soap操作。下面将介绍soap的基本操作。 
一、soap扩展的使用方法 
php的soap扩展库通过soap协议实现了客服端与服务器端的数据交互操作。从php5.0后,php就自带了soap的支持。使用soap扩展库首先需要修改php安装目录下的配置文件php.ini 来激活soap扩展库。在php.ini文件中找到如下所示的一行代码,去掉前面的注释(;)。 ;extension=php_soap.dll修改后,重启web服务器即可激活soap扩展。在soap扩展库中,主要 包括三种对象。 
1、SoapServer 
SoapServer用于创建php服务器端页面时定义可被调用的函数及返回响应数据。创建一个SoapServer对象的语法格式如下: 
$soap = new SoapServer($wsdl,$array); 
其中,$wsdl为shoap使用得wsdl文件,wsdl是描述Web Service的一种标准格式,若将$wsdl设置为null,则表示不使用wsdl模式。$array是SoapServer的属性信息,是一个数组。

SoapServer对象的addFunction方法是用来声明哪个函数可以被客户端调用, 语法格式如下:
$soap-〉addFunction($function_name); 
其中,$soap是一个SoapServer对象,$function_name是需要被调用的函数名。

SoapServer对象的handle方法用来处理用户输入并调用相应的函数,最后返回给客户端处理的结果。语法格式如下: 
$soap-〉handle([$soap_request]); 
其中,$soap是一个SoapServer对象,$soap_request是一个可选参数,用来表示用户的请求信息。如果不指定$soap_request,则表示服务器将接收用户的全部请求。 

2、SoapCliet 
SoapClient用于调用远程服务器上的SoapServer页面,并实现了对相应函数的调用。创建一个SoapClient对象的语法格式如下: 
$soap = new SoapClient($wsdl,$array); 
其中,参数$wsdl和$array与SoapServer相同。创建SoapClient对象后,调用服务端页面中的函数相当于调用了SoapClient的方法,创建语法如下: 
$soap-〉user_function($params); 
其中,$soap是一个SoapClient对象,user_function是服务器端要调用的函数,$params是要传入函数的参数。

3、SoapFault 
SoapFault用于生成soap访问过程中可能出现的错误。创建一个soapFault对象的语法格式
如下: 
$fault = newSoapFault($faultcode,$faultstring); 
其中,$faultcode是用户定义的错误代码,$faultstring是用户自定义的错误信息。soapFault对象会在服务器端页面出现错误时自动生成,或者通过用户自行创建SoapFault对象时生成。对于Soap访问时出现的错误,客户端可通过捕捉SoapFalut对象来获得相应的错误信息。在客户端捕获SoapFault对象后,可以通过下面的代码获得错误代码和错误信息。 
$fault-〉faultcode;//错误代码 
$fault-〉faultstring;//错误信息 
其中,$fault是在前面创建的SoapFault对象。 
目前的PHP AJAX 库很多,如:SAJAX、JPSPAN、xajax、AJASON、flxAJAX、AjaxAC等
server端的代码: server.php

〈?php 
//声明一个函数add() ,并返回它的值 
function add($a,$b){ 
return $a+$b; 

//实例化一个SoapServer对象, 并将add函数注册成为其方法 
$server = newSoapServer(null,array('uri'=〉'http://localhost/'));//指定server端代码的URI(资源标志符)
$server-〉addFunction("add"); $server-〉handle(); ?〉
然后使用client端的代码来调用server端的代码: client的代码也很简单: 如下:
这个是client端的代码client.php
〈?php 
//建立一个参数数组,存储要访问的提供soap服务的计算机的地址与程序
$arrOptions=array('uri'=〉'http://localhost/','location'=〉'http://localhost/soap/server.php', 
//注意: 这个location指定的是server端代码在服务器中的具体位置,我的是在本地根目录下的soap目录中
,'trace'=〉true, ); 
$soapObject = new SoapClient(null,$arrOptions); //实例化客户端对象echo $soapObject-〉add(20,30);//调用服务器端的函数add并返回值50 
?〉

用php5+ 做webservice (php soapwebservice) 
SOAP:简单对象访问协议
  (SOAP:Simple Object Access Protocol)
  简单对象访问协议(SOAP)是一种轻量的、简单的、基于XML 的协议,它被设计成在WEB上交换结构化的和固化的信息。SOAP可以和现存的许多因特网协议和格式结合使用,包括超文本传输协议(HTTP),简单邮件传输协议(SMTP),多用途网际邮件扩充协议(MIME)。它还支持从消息系统到远程过程调用(RPC)等大量的应用程序。
  SOAP 包括四个部分:
  SOAP 封装:它定义了一个框架, 该框架描述了消息中的内容是什么,谁应当处理它以及它是可选的还是必须的。
  SOAP 编码规则:它定义了一种序列化的机制,用于交换应用程序所定义的数据类型的实例。
  SOAP RPC 表示:它定义了用于表示远程过程调用和应答的协定。
  SOAP 绑定:定义了一种使用底层传输协议来完成在节点间交换SOAP封装的约定。
  SOAP 消息基本上是从发送端到接收端的单向传输,但它们常常结合起来执行类似于请求/ 应答的模式。所有的SOAP消息都使用XML 编码。一条SOAP 消息就是一个包含有一个必需的SOAP 的封装包,一个可选的SOAP 标头和一个必需的SOAP体块的XML 文档。
  把SOAP 绑定到HTTP 提供了同时利用SOAP 的样式和分散的灵活性的特点以及HTTP的丰富的特征库的优点。在HTTP 上传送SOAP 并不是说SOAP 会覆盖现有的HTTP 语义,而是HTTP 上的SOAP语义会自然的映射到HTTP 语义。在使用HTTP 作为协议绑定的场合中,RPC 请求映射到HTTP 请求上,而RPC应答映射到HTTP 应答。然而,在RPC 上使用SOAP 并不仅限于HTTP 协议绑定。
  SOAP也可以绑定到TCP和UDP协议上。 
WSDL 简介
  Web Services DescriptionLanguage的缩写,是一个用来描述Web服务和说明如何与Web服务通信的XML语言。 
用php5+ 做webservice
1,首先要设置服务器环境。
修改php.ini 
得添加extension=php_soap.dll (加载soap 内置包) 
修改soap.wsdl_cache_enabled=1改为soap.wsdl_cache_enabled=0 

2,写soap 服务端。(用Zend Studio For Eclipse编写) 
2.1, 写一个用来提供给客户端用的类文件( DizzyLion.php ) 
Class DizzyLion {
/ **
* 求和函数
* @param float $p_a
* @param float $p_b
* @return float 
* /
Public function sum($p_a, $p_b){
Return $p_a + $p_b;
}
}
说明:写上函数的标准注释有利于下面做wsdl的工作。
2.2, 生成wsdl 文件。(dizzylion.wsdl)如果这个你能手写,那你真是太强了。我用zend studio生成的。
我用的Zend Studio for Eclipse 6.1 
选'File'-〉'Export'-〉'PHP'-〉'WSDL File' 
在"Generate WSDL File" 的窗口中。
Configuration name 取自己想设的名字;File name指定要生成wsdl文件(dizzylion.wsdl);Exported files中"Add"添加刚刚的DizzyLion.php类文件;在classer url 就会出现DizzyLion.php的所有类,勾选DizzyLion. 在url写入server.php的WEB访问URL如:http://localhot/server.php。点"finish"就好了。如果有上面的标准注释这里就不用再去设置wsdl里对应参数类型之类了。
2.3, 写Soap 服务端文件(server.php) 
〈?php
Require './DizzyLion.php';
$server = new SoapServer('./dizzylion.wsdl');
$server-〉setClass('DizzyLion');
$server-〉handle();
?〉
3, 写Soap客户端。(client.php) 
〈?php
$soap = new SoapClient('./dizzylion.wsdl');//如果是远程,那当然写dizzylion.wsdl的URL了。
echo $soap-〉sum(1.1, 3.1);
?〉

=======================================================

〈?php
\\**
 *   SoapClientAuth for accessing Web Servicesprotected by HTTP authentication
 *   Author: tc
 *    LastModified: 04/08/2011
 *   Update: 14/03/2012 - Fixed issue withCURLAUTH_ANY not authenticating to NTLM servers
 *   Download from: http://tcsoftware.net/blog/  *
 *   Copyright (C) 2011  tc software(http://tcsoftware.net)
 *
 *    Thisprogram is free software: you can redistribute it and/ormodify
 *    itunder the terms of the GNU General Public License as publishedby
 *    theFree Software Foundation, either version 3 of the License, or
 *    (atyour option) any later version.
 *
 *    Thisprogram is distributed in the hope that it will be useful,
 *    butWITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULARPURPOSE.  See the
 *    GNUGeneral Public License for more details.
 *
 *    Youshould have received a copy of the GNU General Public License
 *    alongwith this program.  If not, see〈http://www.gnu.org/licenses/〉.
 */
 
 
    \\**
     *SoapClientAuth
     * Theinterface and operation of this class is identical to the PHPSoapClient class(http://php.net/manual/en/class.soapclient.php)
     *except this class will perform HTTP authentication for both SOAPmessages and while downloading WSDL over HTTP and HTTPS.
     *Provide the options login and password in the options array of theconstructor.
    * 
     *@author tc
     *@copyright Copyright (C) 2011 tc software
     *@license http://opensource.org/licenses/gpl-license.php GNU PublicLicense
     *@link http://php.net/manual/en/class.soapclient.php      *@link http://tcsoftware.net/     */
    class SoapClientAuthextends SoapClient{
    public $Username =NULL;
    public $Password =NULL;
     
    \\**
    * 
     *@param string $wsdl
     *@param array $options 
    */
    functionSoapClientAuth($wsdl, $options = NULL)
    {
       stream_wrapper_unregister('https');
       stream_wrapper_unregister('http');
       stream_wrapper_register('https','streamWrapperHttpAuth');
       stream_wrapper_register('http','streamWrapperHttpAuth');
        
       if($options)
       {
       $this-〉Username = $options['login'];
       streamWrapperHttpAuth::$Username =$this-〉Username;
       $this-〉Password = $options['password'];
       streamWrapperHttpAuth::$Password =$this-〉Password;
       }
        
       parent::SoapClient($wsdl,($options?$options:array()));
        
       stream_wrapper_restore('https');
       stream_wrapper_restore('http');
    }
     
    function__doRequest($request, $location, $action, $version) {
 
       $headers = array(
          'User-Agent: PHP-SOAP',
          'Content-Type: text/xml; charset=utf-8',
          'SOAPAction: "' . $action . '"',
          'Content-Length: ' . strlen($request),
           'Expect:100-continue',
          'Connection: Keep-Alive'
       );
        
       $this-〉__last_request_headers = $headers;
       $ch = curl_init($location);
       curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
        
       curl_setopt($ch, CURLOPT_POST, TRUE);
       curl_setopt($ch, CURLOPT_POSTFIELDS,$request);
        
       curl_setopt($ch, CURLOPT_HTTP_VERSION,CURL_HTTP_VERSION_1_1);
       curl_setopt($ch, CURLOPT_FAILONERROR,FALSE);
       curl_setopt($ch, CURLOPT_HTTPAUTH,CURLAUTH_ANY);
        
       curl_setopt($ch, CURLOPT_USERPWD,$this-〉Username . ':' . $this-〉Password);
       curl_setopt($ch, CURLOPT_SSLVERSION, 3);
       curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,FALSE);
       curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,2);
        
       curl_setopt($ch, CURLOPT_HTTPHEADER,$headers);
       curl_setopt($ch, CURLOPT_VERBOSE, TRUE);
       curl_setopt($ch, CURLOPT_CERTINFO, TRUE);
        
       $response = curl_exec($ch);
        
       if(($info = curl_getinfo($ch)) &&$info['http_code']==200)
           return$response;
       else if($info['http_code']==401)
           throw newException ('Access Denied', 401); 
       else if(curl_errno($ch)!=0)
       {
           throw newException(curl_error($ch), curl_errno($ch));
       }else
           throw newException('Error', $info['http_code']);
    }
    }
 
    classstreamWrapperHttpAuth
    {
    public static $Username= NULL;
    public static $Password= NULL;
     
    private $path =NULL;
    private $position =0;
    private $buffer =NULL;
    private $curlHandle =NULL;
     
    public functionstream_close()
    {
       if($this-〉curlHandle)
       curl_close ($this-〉curlHandle);
    }
     
    public functionstream_open($path, $mode, $options, &$opened_path)
    {
       $this-〉path = $path;
       $response =$this-〉postRequest($this-〉path);
       $this-〉buffer =($response!==FALSE?$response:NULL);
       $this-〉position = 0;
       return $response!==FALSE;
    }
     
    public functionstream_eof()
    {
       return$this-〉position〉strlen($this-〉buffer);
    }
     
    public functionstream_flush()
    {
       $this-〉position = 0;
       $this-〉buffer = NULL;
    }
     
    public functionstream_read($count)
    {
       if($this-〉buffer)
       {
       $data = substr($this-〉buffer, $this-〉position,$count);
       $this-〉position += $count;
       return $data;
       }
       return FALSE;
    }
     
    public functionstream_write($data)
    {
       return ($this-〉buffer?TRUE:FALSE);
    }
     
    public functionstream_seek($offset, $whence = SEEK_SET)
    {
       switch($whence)
       {
       case SEEK_SET:
          $this-〉position = $offset;
          break;
       case SEEK_CUR:
          $this-〉position += $offset;
          break;
       case SEEK_END:
          $this-〉position = strlen($this-〉buffer) + $offset;
          break;
       }
        
       return TRUE;
    }
     
    public functionstream_tell()
    {
       return $this-〉position;
    }
     
    public functionstream_stat()
    {
       return array('size' =〉strlen($this-〉buffer));
    }
     
    public functionurl_stat($path, $flags)
    {
       $response = $this-〉postRequest($path);
       return array('size' =〉 strlen($response));
    }
     
    protected functionpostRequest($path, $authType = CURLAUTH_ANY)
    {
       $this-〉curlHandle = curl_init($path);
       curl_setopt($this-〉curlHandle,CURLOPT_RETURNTRANSFER, TRUE);
       curl_setopt($this-〉curlHandle,CURLOPT_FOLLOWLOCATION, TRUE);
       if(streamWrapperHttpAuth::$Username)
       {
       curl_setopt($this-〉curlHandle, CURLOPT_HTTPAUTH,$authType);
       curl_setopt($this-〉curlHandle, CURLOPT_USERPWD,streamWrapperHttpAuth::$Username . ':' .streamWrapperHttpAuth::$Password);
       }
        
       curl_setopt($this-〉curlHandle,CURLOPT_SSLVERSION, 3);
       curl_setopt($this-〉curlHandle,CURLOPT_SSL_VERIFYPEER, FALSE);
       curl_setopt($this-〉curlHandle,CURLOPT_SSL_VERIFYHOST, 2);
        
       $response = curl_exec($this-〉curlHandle);
        
       if(($info = curl_getinfo($this-〉curlHandle))&& $info['http_code']==200)
       {
       if(curl_errno($this-〉curlHandle)==0)
       {
           return$response;
       }else
           throw newException(curl_error($this-〉curlHandle),curl_errno($this-〉curlHandle));
       }else if($info['http_code']==401)
       { // Attempt NTLM Auth only, CURLAUTH_ANY doesnot work with NTML
       if($authType!=CURLAUTH_NTLM)
           return$this-〉postRequest($path, CURLAUTH_NTLM);
       else
       {
           throw newException ('Access Denied', 401);
       }
       }else if(curl_errno($this-〉curlHandle)!=0)
       {
           throw newException(curl_error($this-〉curlHandle),curl_errno($this-〉curlHandle));
       }else
       throw new Exception('Error',$info['http_code']);
 
       return FALSE;
    }
    }
?〉
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  soap