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;
}
}
?〉