php soap编码转换
在给CHINAZ资讯(dedecms)做同步bbsmax passport登陆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的英文文档,那是个云里雾里。收集了一些资料,做了一个关于saop header进行身份认证的实验。
在这个实验中,假定soap client用一个字符串作为身份认证的标识,soap server取到这个字符串后,对其进行辨认,如果与期望相符合,认证通过,如果不符,抛出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 = new SoapServer(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 = new SoapServer(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 = new SoapFault($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 = new SoapServer(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 soap webservice)
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 Description Language的缩写,是一个用来描述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 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; } } ?>