php写soap难么,关于soap的一些小知识

本文详细探讨了PHP SOAP服务中遇到的UTF-8编码问题、类服务提供、错误通知、对象持久化、数据传递、classmap与typemap的使用,以及相关测试案例。特别关注了如何处理异常、uri与location的区别,以及如何在实践中正确配置 SoapClient 和 SoapServer。

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 错误,然后就是调试半天,百思不得姐。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值