1. soap编码问题
soap采用utf-8编码作为传输的字符集编码,至少php的soap的实现是写死成utf-8字符编码的,如果server端或client端使用的是非utf-8的字符编码时,可能存在转码问题,导致转码错误; 一个不太正规的做法是: 在server端和client端都对字符做url编码(或别的编码),是的传输的字符都转换成了单字节的ascii字符,这样就不会出现转码问题了。 但是,如果访问量很大,会存在流量和效率方面的问题,如果访问量很小,还是可以使用的
2. 让多个类提供服务的方法,如:
$server = new SoapServer();
$class = $_GET["classname"];
$server->setClass("$class");
$server->handle();
1
2
3
4
5
$server=newSoapServer();
$class=$_GET["classname"];
$server->setClass("$class");
$server->handle();
3. 错误处理
server端的错误如何告知client呢?通过 $server->fault() 来实现,如:
$server = new SoapServer();
try{
$server->setClass("Phpor");
$server->handle();
}catch(Exception $e) {
$server->fault($e->getCode(), $e->getMessage());
}
1
2
3
4
5
6
7
8
$server=newSoapServer();
try{
$server->setClass("Phpor");
$server->handle();
}catch(Exception$e){
$server->fault($e->getCode(),$e->getMessage());
}
client 端实现:
$client = new SoapClient(...);
try {
$client->test();
} catch(SoapFault $e) {
throw new Exception($e->faultstring, $e->faultcode);
}
1
2
3
4
5
6
7
$client=newSoapClient(...);
try{
$client->test();
}catch(SoapFault$e){
thrownewException($e->faultstring,$e->faultcode);
}
1) 根据 ext/soap/soap.c 中关于$server->handle() 的实现来看,如果使用的是ZEND_ENGINE_2 ,则在执行的函数中 throw new SoapFault(…) 也会有和上述相同的效果的,如:
$server = new SoapServer();
$server->addFunction("Phpor");
$server->handle();
function Phpor() {
// 注意,如果是一个别的异常,如Exception ,将不会自动转换为一个有效的soap错误,而是返回500错误,而且没有任何soap的xml信息,如果是一个SoapFault的子类或许是可以的,当然,似乎也没这么做的必要
throw new SoapFault("Server", "something wrong");
}
1
2
3
4
5
6
7
8
9
$server=newSoapServer();
$server->addFunction("Phpor");
$server->handle();
functionPhpor(){
// 注意,如果是一个别的异常,如Exception ,将不会自动转换为一个有效的soap错误,而是返回500错误,而且没有任何soap的xml信息,如果是一个SoapFault的子类或许是可以的,当然,似乎也没这么做的必要
thrownewSoapFault("Server","something wrong");
}
可以通过:
# php -i | grep -i engine
来确认使用的Zend Engine 的版本
2) 虽然soapclient可以识别一些http的状态码,但是soapserver:fault 产生错误时,http状态码统一为500,注意,当user-agent 为 Shockwave Flash 时,依然使用200 的状态码返回,大概是对于flash实现soap时特殊情况的兼容吧。参看php: ext/soap/soap.c 中 函数: soap_server_fault_ex(…) 的实现,php的soapclient可以通过如下方式设置user-agent:
$client = new SoapClient("",array("user_agent"=>"agent_name"));
1
2
$client=newSoapClient("",array("user_agent"=>"agent_name"));
4. 对象的持久化
对象的持久化就是在session中保存类或对象的一些数据信息;要知道连接句柄等和进程相关的资源信息是无法有效地保存的
5. 数据的传递
如果你执行的server端的函数为 echo “hello world”; 那么client将得不到该信息,参看php的soap扩展中soap.c 有相关代码如下:
...
if (php_start_ob_buffer(NULL, 0, 0 TSRMLS_CC) != SUCCESS) {
php_error_docref(NULL TSRMLS_CC, E_ERROR,"ob_start failed");
}
...
1
2
3
4
5
...
if(php_start_ob_buffer(NULL,0,0TSRMLS_CC)!=SUCCESS){
php_error_docref(NULLTSRMLS_CC,E_ERROR,"ob_start failed");
}
...
6. 什么叫classmap?
在使用SoapClient时,允许通过如下方式设置classmap,如:
$c = new SoapClient(null, array(
"location"=>"http://phpor.net/soap.php",
"uri"=>"http://phpor.net/",
"classmap"=>array("Struct"=>"phpor")
));
$phpor = $c->test();
var_dump($phpor);
echo $phpor->get_name();
class phpor{
public function get_name() {
return $this->name;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$c=newSoapClient(null,array(
"location"=>"http://phpor.net/soap.php",
"uri"=>"http://phpor.net/",
"classmap"=>array("Struct"=>"phpor")
));
$phpor=$c->test();
var_dump($phpor);
echo$phpor->get_name();
classphpor{
publicfunctionget_name(){
return$this->name;
}
}
因为在server端,如果return一个server端的对象,则被转换为一个Struct的类型,内部含有该对象的一些数据信息,当然,对象的方法信息都是没有的; 该类在client端可能就是不存在的,这时候就可以map到client端定义的一个类,而且可以通过该类的方法来访问对象内部的一些信息。
但是,有一点需要注意,client端不要定义和server端的类相同的属性信息,如果定义了,则该属性将成为一个数组,包含client端和server端的信息。不知道为什么这么设计。
7. 什么叫typemap?
$c = new SoapClient(null, array(
"location"=>"http://phpor.net/soap.php",
"uri"=>"http://phpor.net/",
"typemap"=>array(
array(
"type_name"=>"test",
"type_ns"=>"http://phpor.net/",
"from_xml"=>"from_xml",
"to_xml"=>"to_xml",
)
),
));
//...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$c=newSoapClient(null,array(
"location"=>"http://phpor.net/soap.php",
"uri"=>"http://phpor.net/",
"typemap"=>array(
array(
"type_name"=>"test",
"type_ns"=>"http://phpor.net/",
"from_xml"=>"from_xml",
"to_xml"=>"to_xml",
)
),
));
//...
8. 关于soap的测试,单个脚本实现的SoapServer和SoapClient:
function Add($x,$y) {
return $x+$y;
}
class LocalSoapClient extends SoapClient {
function __construct($wsdl, $options) {
parent::__construct($wsdl, $options);
$this->server = new SoapServer($wsdl, $options);
$this->server->addFunction('Add');
}
function __doRequest($request, $location, $action, $version, $one_way = 0) {
ob_start();
$this->server->handle($request);
$response = ob_get_contents();
ob_end_clean();
return $response;
}
}
$x = new LocalSoapClient(NULL,array('location'=>'test://',
'uri'=>'http://testuri.org'));
var_dump($x->Add(3,4));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
functionAdd($x,$y){
return$x+$y;
}
classLocalSoapClientextendsSoapClient{
function__construct($wsdl,$options){
parent::__construct($wsdl,$options);
$this->server=newSoapServer($wsdl,$options);
$this->server->addFunction('Add');
}
function__doRequest($request,$location,$action,$version,$one_way=0){
ob_start();
$this->server->handle($request);
$response=ob_get_contents();
ob_end_clean();
return$response;
}
}
$x=newLocalSoapClient(NULL,array('location'=>'test://',
'uri'=>'http://testuri.org'));
var_dump($x->Add(3,4));
9. 关于数据类型
函数的参数和返回值是涉及到数据类型的,如果仅仅使用string、int、array,则基本是没有问题的,如果使用到了对象,就需要注意了,可能需要用到classmap、typemap了,还有SoapParam、SoapVar; 稍后再研究
10. 关于location和uri
在new SoapClient和new SoapServer时都需要uri,而且这两个uri也是可以不一样的,其实程序根本不会去访问这个uri,这个uri只是xml中的一个名字空间的名字; 而new SoapClient时的那个location却是真正要访问的soapserver的url地址;但是,需要注意的是: uri 最好不要带参数,带一个参数还好,如果要带多个,必然出现 ‘&’,而uri是要写到请求的xml中的,xml中出现 ‘&’,会被解释为实体,然而却不是一个合法的实体,所以就会出现 400 错误,然后就是调试半天,百思不得姐。
本文详细探讨了PHP SOAP服务中遇到的UTF-8编码问题、类服务提供、错误通知、对象持久化、数据传递、classmap与typemap的使用,以及相关测试案例。特别关注了如何处理异常、uri与location的区别,以及如何在实践中正确配置 SoapClient 和 SoapServer。
1万+

被折叠的 条评论
为什么被折叠?



