PHP 生成WSDL 以及 提供SOAP服务

近期项目需要与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>');
    }
}

3. 服务端 生成WSDL的代码如下

  $soap = new soapDiscovery();
        header("Content-type:text/xml");

        //此处需要根据SAOP服务器地址进行修改
        echo $soap->getWSDL('MSupervise', 'cmdService','http://www.tgj.com/supervise/process');

   这里 的参数1, 是 你用来处理SOAP请求的类的名称,请事先加载类

       参数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;
    }
}

    在调用时,使用这个子类, 即可在这个扩展类中加入打印等调试语句

你可能感兴趣的:(PHP 生成WSDL 以及 提供SOAP服务)