近期项目需要与Java对接, 为 Java方提供 SOAP服务,
我们知道SOAP服务 分为 有WSDL与没有WSDL的两种.
我们实现了较为复杂的有WSDL的这种
1.注意要点: PHP环境中 php.ini 中always_populate_raw_post_data 必须设置为 -1 (默认不是)
否则会出现 各种错误.
2.网上找了SoapDiscovery.class.php ,使用后,只有一点不太好用, 就是 location地址不合适,我对这个类进行了改造,代码下附
<?php /** * 生成WSDL的SOAP类,修改版 **/ class soapDiscovery { /** * 构造方法检查PHP.INI设置 * soapDiscovery constructor. */ public function __construct() { if(ini_get('always_populate_raw_post_data')!=-1){ throw new Exception('php.ini setting always_populate_raw_post_data must be -1'); } } /** * 生成 k="v" ... 的属性串 * @param $array array 属性键值数组 * @return string 属性串 */ private function kvs(array $array) { $ret = ''; foreach ($array as $k => $v) { $ret .= ' ' . $k . '="' . $v . '"'; } return $ret; } /** * 生成一个标签 * @param $name string 标签名称 * @param $open bool 是否开放标签(闭合标签自带/>) * @param array $properties 属性键值数组 * @return string 标签的字符串表达 */ private function tag($name, $open, array $properties) { return '<' . $name . ' ' . $this->kvs($properties) . ($open ? '' : '/') . '>' . "\n"; } /** * 生成WSDL * @param string $class_name 服务类名 * @param string $service_name 服务名称 * @param string $location 提供服务的地址 * @return string * @throws **/ public function getWSDL($class_name, $service_name, $location) { //对处理响应的类 反射 $class = new ReflectionClass($class_name); if (!$class->isInstantiable()) { throw new Exception('Class is not instantiable.'); } //换行符 $n = "\n"; $schemas = 'http://schemas.xmlsoap.org/'; //头部空间定义 $headerWSDL = '<' . '?xml version="1.0" ?' . '>' . $n; $headerWSDL .= $this->tag('definitions', true, [ 'name' => $service_name, 'targetNamespace' => 'urn:' . $service_name, 'xmlns:wsdl' => $schemas . 'wsdl/', 'xmlns:soap' => $schemas . 'wsdl/soap/', 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema', 'xmlns:soap-ENC' => $schemas . 'soap/encoding', 'xmlns' => $schemas . 'wsdl/' ]); $headerWSDL .= $this->tag('types', false, ['xmlns' => $schemas . 'wsdl/']); $portTypeWSDL = $this->tag('portType', true, ['name' => $service_name . 'Port']); $bindingWSDL = $this->tag('binding', true, [ 'name' => $service_name . 'Binding', 'type' => 'tns:' . $service_name . 'Port' ]) . $this->tag('soap:binding', false, [ 'style' => 'rpc', 'transport' => $schemas . 'soap/http' ]); $serviceWSDL = $this->tag('service', true, ['name' => $service_name]) . $this->tag('documentation', false, []) . $this->tag('port', true, [ 'name' => $service_name . 'Port', 'binding' => 'tns:' . $service_name . 'Binding' ]) . $this->tag('soap:address', false, ['location' => $location]) . '</port>' . $n . '</service>' . $n; $body = $this->tag('soap:body', false, [ 'use' => 'encoded', 'namespace' => 'urn:' . $service_name, 'encodingStyle' => $schemas . 'soap/encoding/' ]); $messageWSDL = ''; $methods = $class->getMethods(); foreach ($methods as $method) { if (!$method->isPublic() or $method->isConstructor()) continue; $methodName = $method->getName(); $portTypeWSDL .= $this->tag('operation', true, ['name' => $methodName]) . $this->tag('input', false, ['message' => 'tns:' . $methodName . 'Request']) . $this->tag('output', false, ['message' => 'tns:' . $methodName . 'Response']) . '</operation>' . $n; $bindingWSDL .= $this->tag('operation', true, ['name' => $methodName]) . $this->tag('soap:operation', false, ['soapAction' => 'urn:' . $service_name . '#' . $class_name . '#' . $methodName]) . '<input>' . $n . $body . '</input>' . $n . '<output>' . $n . $body . '</output>' . $n . '</operation>' . $n; $messageWSDL .= $this->tag('message', true, ['name' => $methodName.'Request']); $parameters = $method->getParameters(); foreach ($parameters as $parameter) { $messageWSDL .= $this->tag('part', false, [ 'name' => $parameter->getName(), 'type' => 'xsd:string' ]); } $messageWSDL .= '</message>' . $n . $this->tag('message', true, ['name' => $methodName . 'Response']) . $this->tag('part', false, ['name' => $methodName, 'type' => 'xsd:string']) . "</message>" . $n; } $portTypeWSDL .= "</portType>".$n; $bindingWSDL .= "</binding>".$n; return sprintf('%s%s%s%s%s%s', $headerWSDL, $portTypeWSDL, $bindingWSDL, $serviceWSDL, $messageWSDL, '</definitions>'); } }
$soap = new soapDiscovery(); header("Content-type:text/xml"); //此处需要根据SAOP服务器地址进行修改 echo $soap->getWSDL('MSupervise', 'cmdService','http://www.tgj.com/supervise/process');
参数2. 是 一个 服务名称的标识,请自行定义, 只要在服务端与客户端保持一致即可
参数3. 是处理SOAP请求的地址(服务端)
生成WSDL后,显示在浏览器上, 如果需要保存成文件 ,注意: 查看源代码后再复制保存.否则 会 丢失第一行XML定义
4.客户端 调用示例如下
public function test(){ ini_set("soap.wsdl_cache_enabled",0); ini_set('soap.wsdl_cache_ttl',0); $wsdl='http://www.tgj.com/wsdl/supervise.wsdl?wsdl'; $soap=new SSoapClient($wsdl,[ 'uri'=>'cmdService', 'trace'=>1, "style" => SOAP_RPC, "use" => SOAP_ENCODED ]); try{ $ret=$soap->command(); dump($ret); }catch(Exception $e){ dump($e); } }其中 两行ini_set在开发调试时使用,上线时去除.
$wsdl是一个可以访问到 服务端保存的那个WSDL文件 的地址
uri是服务标识符,要与服务端那个标识符保持一致
5.如果程序运行不通, 调试会比较麻烦
我查找 网上资料后得到 一个方法,扩展了 SOAPClient类
<?php /** * 扩展SOAPClient类,以便加调试代码 * User: 蓝冰 * Date: 2017/5/25 * Time: 11:44 */ class SSoapClient extends SoapClient { public function __doRequest($request, $location, $action, $version, $one_way = 0) { $request = parent::__doRequest($request, $location, $action, $version, $one_way); //dump($request);exit; return $request; } }