php soap编码转换
在给CHINAZ资讯(dedecms)做同步bbsmax passport登陆api时,因为bbsmax使用utf-8编码,而资讯这边用的是GBK编码,导致乱码。开始想自己转码,但有点麻烦。后面想SOAP既然用来针对不同平台,那肯定也包括编码问题,就又认真看了PHP手册,介绍里虽然没有提到,却发现Examples里有,访问很简单。
client=newSoapClient("some.wsdl",array(′encoding′=>′GBK′));只要这么简单,剩下的PHP自己帮忙实现了!通过SoapHeader实现身份认证之前一直抱怨php的soap很傻,在client端有设置header的方法,在server端却没有取header的方法。那是很傻很天真,直接用正则表达式从soap信封的header中提取header信息。最近由于有项目要发布webservice,重新燃起对soap的兴趣,看了w3的英文文档,那是个云里雾里。收集了一些资料,做了一个关于saopheader进行身份认证的实验。在这个实验中,假定soapclient用一个字符串作为身份认证的标识,soapserver取到这个字符串后,对其进行辨认,如果与期望相符合,认证通过,如果不符,抛出soapFault。理论就不多说了,我也不懂,直接上代码client.php<?phpcli = new SoapClient(null, array('uri' => 'http://127.0.0.1/namespace/', 'location' => 'http://localhost/server.php', 'trace' => true));
h=newSoapHeader(′http://127.0.0.1/namespace/′,′auth′,′123456789′,false,SOAPACTORNEXT);cli->__setSoapHeaders(array(h));tryecho$cli−>say();catch(Exceptione) {
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 <?phpsoap_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=newSoapClient(wsdl);
client->__setSoapHeaders(array(soap_header));
client->__soapCall('request', array()); ?> 现在, 客户端已经发起了请求, 请求中也包含了SoapHeader, 其中有了我们验证需要的用户名/密码信息. 那么, 在服务端, 该如何做呢? <?phpserver = 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∗/privateauthorized = 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, 比如: <?phpserver = new SoapServer(wsdl,array(′actor′=>′laruence′));?>这样,我们就可以在Client的SoapHeader中,通过设置actor是laruence,来让指定的Server来获得我们设置的头部的信息.phpSOAP实现Web服务例子php先要开启phpsoap模块一。方法1服务器端文件叫server.php<?phpsoap = new SoapServer(null,array('uri'=>"http://10.10.10.24/"));//输入本台服务器的ip地址
soap−>addFunction(′say′);//添加输出函数soap->addFunction(SOAP_FUNCTIONS_ALL); //不要忘了这个
soap−>handle();//注意functionsay(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 (SoapFaultfault){
echo "Error: ",fault−>faultcode,",string:",fault->faultstring;
}
?>
二。
服务器端文件server.php:
<?php
classmap=array();//注意和实例一的不同soap = new SoapServer(null,array('uri'=>"http://10.10.10.24/", "classmap" => classmap));soap->setClass('Myclass');
soap−>handle();classMyclassfunctionsay($someword)return$someword;?>客户端输出的是xyzworld<?try$client=newSoapClient(null,array(′location′=>"http://www.xiao688.com/server.php",′uri′=>"http://www.xiao688.com/"));vardump($client);echo$client−>say("xyzworld");catch(SoapFaultfault){
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=newSoapServer(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是一个可选参数,用来表示 用户的请求信息。如果不指定soaprequest,则表示服务器将接收用户的全部请求。2、SoapClietSoapClient用于调用远程服务器上的SoapServer页面,并实现了对相应函数的调用。创建一个SoapClient对象的语法格式如下:soap = new SoapClient(wsdl,array);
其中,参数wsdl和array与SoapServer相同。 创建SoapClient对象后,调用服务端页面中的函数相当于调用了SoapClient的方法, 创建语法如下:
soap−>userfunction(params);
其中,soap是一个SoapClient对象,userfunction是服务器端要调用的函数,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=newSoapClient(null,arrOptions); //实例化客户端对象 echo soapObject−>add(20,30);//调用服务器端的函数add并返回值50?>用php5+做webservice(phpsoapwebservice)SOAP:简单对象访问协议 (SOAP:SimpleObjectAccessProtocol) 简单对象访问协议(SOAP)是一种轻量的、简单的、基于XML的协议,它被设计成在WEB上交换结构化的和固化的信息。SOAP可以和现存的许多因特网协议和格式结合使用,包括超文本传输协议(HTTP),简单邮件传输协议(SMTP),多用途网际邮件扩充协议(MIME)。它还支持从消息系统到远程过程调用(RPC)等大量的应用程序。 SOAP包括四个部分: SOAP封装:它定义了一个框架,该框架描述了消息中的内容是什么,谁应当处理它以及它是可选的还是必须的。 SOAP编码规则:它定义了一种序列化的机制,用于交换应用程序所定义的数据类型的实例。 SOAPRPC表示:它定义了用于表示远程过程调用和应答的协定。 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简介 WebServicesDescriptionLanguage的缩写,是一个用来描述Web服务和说明如何与Web服务通信的XML语言。用php5+做webservice1,首先要设置服务器环境。修改php.ini得添加extension=phpsoap.dll(加载soap内置包)修改soap.wsdlcacheenabled=1改为soap.wsdlcacheenabled=02,写soap服务端。(用ZendStudioForEclipse编写)2.1,写一个用来提供给客户端用的类文件(DizzyLion.php)ClassDizzyLion/∗∗∗求和函数∗@paramfloat$pa∗@paramfloat$pb∗@returnfloat∗/Publicfunctionsum($pa,$pb)Return$pa+$pb;说明:写上函数的标准注释有利于下面做wsdl的工作。2.2,生成wsdl文件。(dizzylion.wsdl)如果这个你能手写,那你真是太强了。我用zendstudio生成的。我用的ZendStudioforEclipse6.1选′File′−>′Export′−>′PHP′−>′WSDLFile′在"GenerateWSDLFile"的窗口中。Configurationname取自己想设的名字;Filename指定要生成wsdl文件(dizzylion.wsdl);Exportedfiles中"Add"添加刚刚的DizzyLion.php类文件;在classerurl就会出现DizzyLion.php的所有类,勾选DizzyLion.在url写入server.php的WEB访问URL如:http://localhot/server.php。点"finish"就好了。如果有上面的标准注释这里就不用再去设置wsdl里对应参数类型之类了。2.3,写Soap服务端文件(server.php)<?phpRequire′./DizzyLion.php′;server = new SoapServer('./dizzylion.wsdl');
server−>setClass(′DizzyLion′);server->handle();
?>
3, 写Soap客户端。(client.php)
<?php
soap=newSoapClient(′./dizzylion.wsdl′);//如果是远程,那当然写dizzylion.wsdl的URL了。echosoap->sum(1.1, 3.1);
?>
=======================================================
<?php /** * SoapClientAuth for accessing Web Services protected by HTTP authentication * Author: tc * Last Modified: 04/08/2011 * Update: 14/03/2012 - Fixed issue with CURLAUTH_ANY not authenticating to NTLM servers * Download from: http://tcsoftware.net/blog/ * * Copyright (C) 2011 tc software (http://tcsoftware.net) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /** * SoapClientAuth * The interface and operation of this class is identical to the PHP SoapClient class (http://php.net/manual/en/class.soapclient.php) * except this class will perform HTTP authentication for both SOAP messages and while downloading WSDL over HTTP and HTTPS. * Provide the options login and password in the options array of the constructor. * * @author tc * @copyright Copyright (C) 2011 tc software * @license http://opensource.org/licenses/gpl-license.php GNU Public License * @link http://php.net/manual/en/class.soapclient.php * @link http://tcsoftware.net/ */ class SoapClientAuth extends SoapClient{ public $Username = NULL; public $Password = NULL; /** * * @param string $wsdl * @param array $options */ function SoapClientAuth($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 new Exception ('Access Denied', 401); else if(curl_errno($ch)!=0) { throw new Exception(curl_error($ch), curl_errno($ch)); }else throw new Exception('Error', $info['http_code']); } } class streamWrapperHttpAuth { public static $Username = NULL; public static $Password = NULL; private $path = NULL; private $position = 0; private $buffer = NULL; private $curlHandle = NULL; public function stream_close() { if($this->curlHandle) curl_close ($this->curlHandle); } public function stream_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 function stream_eof() { return $this->position>strlen($this->buffer); } public function stream_flush() { $this->position = 0; $this->buffer = NULL; } public function stream_read($count) { if($this->buffer) { $data = substr($this->buffer, $this->position, $count); $this->position += $count; return $data; } return FALSE; } public function stream_write($data) { return ($this->buffer?TRUE:FALSE); } public function stream_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 function stream_tell() { return $this->position; } public function stream_stat() { return array('size' => strlen($this->buffer)); } public function url_stat($path, $flags) { $response = $this->postRequest($path); return array('size' => strlen($response)); } protected function postRequest($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 new Exception(curl_error($this->curlHandle), curl_errno($this->curlHandle)); }else if($info['http_code']==401) { // Attempt NTLM Auth only, CURLAUTH_ANY does not work with NTML if($authType!=CURLAUTH_NTLM) return $this->postRequest($path, CURLAUTH_NTLM); else { throw new Exception ('Access Denied', 401); } }else if(curl_errno($this->curlHandle)!=0) { throw new Exception(curl_error($this->curlHandle), curl_errno($this->curlHandle)); }else throw new Exception('Error', $info['http_code']); return FALSE; } } ?>