由于需要与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()}";
}
响应如下:
