Laravel php Soap Server Client使用笔记

由于需要与ESB通信,所以需要在Laravel使用SOAP Server和SOAP Client。

soap lib: php默认库php_soap.dll

php版本:php 7.1 x64 ts

Laravel版本:5.4

一、SOAP Server

1.1 WSDL模式

wsdl模式下,url形式如/sync?wsdl,当使用浏览器直接访问/sync?wsdl时,返回接口描述,当使用soapClient访问/sync?wsdl时,可以调用其内部方法。

当为wsdl模式时,wsdl描述需要保存为wsdl文件供SoapServer()函数初始化使用,初始化形式如下:

$server = new SoapServer("some.wsdl");

$server = new SoapServer("some.wsdl", array('soap_version' => SOAP_1_2));

$server = new SoapServer("some.wsdl", array('actor' => "http://example.org/ts-tests/C"));

$server = new SoapServer("some.wsdl", array('encoding'=>'ISO-8859-1'));

1.1.1 生成wsdl文件

博主不想手写wsdl描述文件,所以使用了百度上的一段代码自动生成(有改动),wsdl文件存储在Laravel storage\wsdl目录,当接口有改动时,需要手动删除此文件,代码如下。

SoapDiscovery类代码如下,用于生成wsdl描述:

<?php
/**
 * Created by PhpStorm.
 * User: claves
 * Date: 11/20/2017
 * Time: 5:27 PM
 */

namespace App\Http\Controllers\Integration;
use Exception;


class SoapDiscovery {
    private $class_name = '';
    private $service_name = '';

    /**
     * SoapDiscovery::__construct() SoapDiscovery class Constructor.
     *
     * @param string $class_name
     * @param string $service_name
     **/
    public function __construct($class_name = '', $service_name = '') {
        $this->class_name = $class_name;
        $this->service_name = $service_name;
    }

    /**
     * SoapDiscovery::getWSDL() Returns the WSDL of a class if the class is instantiable.
     *
     * @return string
     **/
    public function getWSDL() {
        if (empty($this->service_name)) {
            throw new Exception('No service name.');
        }
        $headerWSDL = "<?xml version=\"1.0\" ?>\n";
        $headerWSDL.= "<definitions name=\"$this->service_name\" targetNamespace=\"urn:$this->service_name\" xmlns:wsdl=\"http://schemas.xmlsoap.org/wsdl/\" xmlns:soap=\"http://schemas.xmlsoap.org/wsdl/soap/\" xmlns:tns=\"urn:$this->service_name\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns=\"http://schemas.xmlsoap.org/wsdl/\">\n";
        $headerWSDL.= "<types xmlns=\"http://schemas.xmlsoap.org/wsdl/\" />\n";

        if (empty($this->class_name)) {
            throw new Exception('No class name.');
        }

        $class = new \ReflectionClass($this->class_name);

        if (!$class->isInstantiable()) {
            throw new Exception('Class is not instantiable.');
        }

        $methods = $class->getMethods();

        $portTypeWSDL = '<portType name="'.$this->service_name.'Port">';
        $bindingWSDL = '<binding name="'.$this->service_name.'Binding" type="tns:'.$this->service_name."Port\">\n<soap:binding style=\"rpc\" transport=\"http://schemas.xmlsoap.org/soap/http\" />\n";
        $serviceWSDL = '<service name="'.$this->service_name."\">\n<documentation />\n<port name=\"".$this->service_name.'Port" binding="tns:'.$this->service_name."Binding\"><soap:address location=\"".url()->current()."\" />\n</port>\n</service>\n";
        $messageWSDL = '';
        foreach ($methods as $method) {
            if ($method->isPublic() && !$method->isConstructor()) {
                $portTypeWSDL.= '<operation name="'.$method->getName()."\">\n".'<input message="tns:'.$method->getName()."Request\" />\n<output message=\"tns:".$method->getName()."Response\" />\n</operation>\n";
                $bindingWSDL.= '<operation name="'.$method->getName()."\">\n".'<soap:operation soapAction="urn:'.$this->service_name.'#'.$this->class_name.'#'.$method->getName()."\" />\n<input><soap:body use=\"encoded\" namespace=\"urn:$this->service_name\" encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" />\n</input>\n<output>\n<soap:body use=\"encoded\" namespace=\"urn:$this->service_name\" encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" />\n</output>\n</operation>\n";
                $messageWSDL.= '<message name="'.$method->getName()."Request\">\n";
                $parameters = $method->getParameters();
                foreach ($parameters as $parameter) {
                    $messageWSDL.= '<part name="'.$parameter->getName()."\" type=\"xsd:string\" />\n";
                }
                $messageWSDL.= "</message>\n";
                $messageWSDL.= '<message name="'.$method->getName()."Response\">\n";
                $messageWSDL.= '<part name="'.$method->getName()."\" type=\"xsd:string\" />\n";
                $messageWSDL.= "</message>\n";
            }
        }
        $portTypeWSDL.= "</portType>\n";
        $bindingWSDL.= "</binding>\n";
        return sprintf('%s%s%s%s%s%s', $headerWSDL, $portTypeWSDL, $bindingWSDL, $serviceWSDL, $messageWSDL, '</definitions>');
    }

    /**
     * SoapDiscovery::getDiscovery() Returns discovery of WSDL.
     *
     * @return string
     **/
    public function getDiscovery() {
        return "<?xml version=\"1.0\" ?>\n<disco:discovery xmlns:disco=\"http://schemas.xmlsoap.org/disco/\" xmlns:scl=\"http://schemas.xmlsoap.org/disco/scl/\">\n<scl:contractRef ref=\"".url()->current().$this->path."?wsdl\" />\n</disco:discovery>";
    }
}

1.1.2 监听

SyncSoapServer代码如下,SoapServer主程序:

<?php
/**
 * Created by PhpStorm.
 * User: think
 * Date: 11/20/2017
 * Time: 2:39 PM
 */
namespace App\Http\Controllers\Integration;
use App\Http\Controllers\BasicController;
use Illuminate\Http\Request;
use DB;
use Auth;
use App\Http\Requests;
use Exception;
use Log;
use App\Core\LogEvent;
class SyncSoapServer extends BasicController
{
    private $sysName = "default";
    /**
     *
     * SyncSoapServer constructor.
     */
    public function __construct(){
        $this->sysName = env("APP_NAME");
    }
    public function bind()
    {
        try{
            $procClass = "App\Http\Controllers\Integration\SyncSoapCore";

            $classNameFull = explode('\\', $procClass);
            $className =  array_pop($classNameFull);
            // Step. 判断wsdl文件在不在,不在则创建
            if(!file_exists(storage_path()."/wsdl/".$className.".wsdl"))
            {
                // 判断文件夹在不在,不在则创建
                if(!file_exists(storage_path()."/wsdl/"))
                {
                    mkdir(storage_path()."/wsdl/",0777,true);
                }
                // 获取wsdl描述
                $soapDiscovery = new SoapDiscovery($procClass,"prms_sync");
                $file = fopen(storage_path()."/wsdl/".$className.".wsdl", "w");
                fwrite($file, $soapDiscovery->getWSDL());
                fclose($file);
            }
            $server = new \SoapServer(storage_path()."/wsdl/".$className.".wsdl", array('soap_version'=>SOAP_1_2));
            $server->setClass($procClass);
            $server->handle();
        }
        catch (Exception $e)
        {
            return $this->status(false);
        }
    }
   
}

1.1.3 业务代码

SyncSoapCore类代码如下,用于提供业务逻辑:

<?php
/**
 * Created by PhpStorm.
 * User: think
 * Date: 11/20/2017
 * Time: 6:55 PM
 */

namespace App\Http\Controllers\Integration;


class SyncSoapCore
{

    public function OrgSyncService($ReqMsg)
    {
        return $ReqMsg;
    }
    public function UserSyncService($ReqMsg)
    {

    }


}

1.1.3 web.php路由设置

Route::group([
    'prefix'=>'/integration'
], function () {
    Route::any('/sync', 'Integration\SyncSoapServer@bind');
});

 

1.2 非WSDL模式

经过博主测试(不一定正确)非wsdl模式访问时,不能以/sync?wsdl形式的url使用soapclient访问,必须以/sync形式url访问,否则会提示版本错误。

当为非wsdl模式时,不用保存wsdl文件,SoapServer()初始化形式如下:

$server = new SoapServer(null, array('uri' => "http://test-uri/"));

二、SoapClient

2.1 WSDL模式

2.1.1 非Laravel

SoapClient代码如下:

<?php
/**
 * Created by PhpStorm.
 * User: think
 * Date: 11/20/2017
 * Time: 4:38 PM
 */

$client = new SoapClient(null,
        array('location' =>"http://127.0.0.1:8080/integration/sync",'uri' => "http://127.0.0.1:8080/")
		);
try{
    $result = $client->OrgSyncService("fdsa"); 
    print "The answer is: $result";
}catch(SoapFault $e){
    print "Sorry an error was caught executing your request: {$e->getMessage()}";
}

响应如下:

2.1.2 Laravel