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