I am not certain how to do this. The specs for the SOAP service I am writing says it needs to send back an acknowledgement message even before it responds with the requested response to the request.
How is this done in PHP? I have seen no examples of how to do this.
From the requirements doc:
One acknowledgement message is sent by Integration Partner to Vendor for every SubmitInv message request. A single acknowledgement message is also sent by Vendor to the Integration Partner from every RequestInv message respons
This is NOT a standard TCP ack response. It is a custom SOAP formatted response that is their acknowledgement that the request was received. See example below.
After questioning the vendor:
They claim that it is a legacy system and it was written to process in that flow. They cannot, at this time, change it. I told him that in 20+ yrs programming, I have NEVER seen any SOAP system require an ACK. He claimed that it had to do with having to "wait" for the responses. Apparently they don't understand how to properly handle stateless processing.
I have already attempted to do it using the PHP Output Buffering functions as outlined below by FoxVSky, it does not work in a SOAP transaction. Also, the standard SOAP library, the one built-in to PHP, nor the Zend SOAP library have a feature to do this.
Example:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <PAddRs> <RqUID>f11958c8-3fde-42ca-bd94-94fdfca316ef</RqUID> <PKey>46dba062-2105-4851-831f-a1d364741329</PKey> <AppStatus> <AppStatusCode>Accept</AppStatusCode> </AppStatus> </PAddRs> </soap:Body> </soap:Envelope>
3 Answers
Answers 1
Ok, I have implemented the acknowledgement messaging in my SOAP service, here is how it is called from the client:
<?php require_once __DIR__ . '/vendor/autoload.php'; $options = array(); $options['cache_wsdl'] = WSDL_CACHE_NONE; $options['soap_version'] = SOAP_1_2; $client = new Zend\Soap\Client("http://localhost/soap/server.php?wsdl", $options); try { // Currently loading example request $xml = simplexml_load_file('RequestExample.xml'); $t_xml = new DOMDocument(); $t_xml->loadXML($xml->asXML()); $xml = $t_xml->saveXML($t_xml->documentElement); $response = $client->ReqInv($xml); } catch (Exception $e) { $response = 'Exception: '. $e. "\n"; } echo $response;
And my service:
<?php require_once __DIR__ . '/vendor/autoload.php'; require(__DIR__ . '/PTResp.php'); use Zend\Soap\AutoDiscover; use Zend\Soap\Server; use Zend\Soap\Wsdl; class PT { /** * function ReqInv * Function to return the inventory for the passed request. * * @param string $request * @return string */ function ReqInv($request) { $pt = new PTResp($request); return $pt->toString(); } } if (isset($_GET['wsdl'])) { $wsdl = new AutoDiscover(); $wsdl->setUri('http://localhost/soap/server.php'); $wsdl->setClass('PT'); $wsdl->handle(); } else { $server = new Zend\Soap\Server('http://localhost/soap/server.php?wsdl'); $server->setClass('PT'); $server->setEncoding('ISO-8859-1'); $server->handle(); }
And my class (in PTResp.php):
class PT { function __construct($xml) { $this->m = new Mustache_Engine; $this->xml = @simplexml_load_string($xml); $this->xml->registerXPathNamespace(<my namespace info>); $this->SendAck(); $this->BuildResponse(); } // function __construct /* * This is the function that is actually called to return the response to the client. */ function toString() { $domxml = new DOMDocument('1.0'); $domxml->preserveWhiteSpace = false; $domxml->formatOutput = true; $domxml->loadXML($this->response); $this->response = $domxml->saveXML($domxml->documentElement); return $this->response; } // function toString function SendAck() { $this->Status = "Accept"; $xml_post_string = $this->m->render( '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <ProcurementAddRs xmlns=MyNamespaceInfo"> <RqUID>{{RqUID}}</RqUID> <PKey>{{PKey}}</PKey> <ApplicationStatus> <ApplicationStatusCode>{{Status}}</ApplicationStatusCode> </ApplicationStatus> </ProcurementAddRs> </soap:Body> </soap:Envelope>', array("RqUID" =>$this->RqUID, "PKey"=>$this->PKey, "Status"=>$this->Status)); $url = 'http://localhost/soap/gotit.php'; // in this test, it writes the response to a file. I will be sending it to the endpoint from here $this->curl_post_async($url, $xml_post_string); } // function SendAck function curl_post_async($url, $post_string){ $parts=parse_url($url); $fp = fsockopen($parts['host'], isset($parts['port'])?$parts['port']:80, $errno, $errstr, 30); $out = "POST ".$parts['path']." HTTP/1.1\r\n"; $out.= "Host: ".$parts['host']."\r\n"; $out.= "Content-Type: text/xml\r\n"; $out.= "Content-Length: ".strlen($post_string)."\r\n"; $out.= "Connection: Close\r\n\r\n"; if (isset($post_string)) $out.= $post_string; fwrite($fp, $out); fclose($fp); } // function curl_post_async function BuildResponse() { $this-response = "<XML response that is built goes here>"; } }
Answers 2
If you're writing a SOAP service, why not simply use the SoapService along with the handle() method? You shouldn't need to actually implement the TCP handshake (sending ACK
) and what not. I'm pretty sure that's all handled for you in those classes/methods.
The context is lacking, so this is my best educated guess.
Answers 3
You can use PHP Output Control Functions
Here my example usage
soapserver.php:
<?php $data = file_get_contents('php://input'); $fp = fopen('data.txt', 'a'); fwrite($fp, json_encode($data)); fwrite($fp, "\n"); class MySoapServer { public function addNumbers($num1, $num2) { return $num1 + $num2; } } $options = array('uri' => 'http://test.local/'); $server = new SoapServer(NULL, $options); $server->setClass('MySoapServer'); ob_end_clean(); header("Connection: close\r\n"); ignore_user_abort(true); ob_start(); $server->handle(); $soapXml = ob_get_contents(); $size = ob_get_length(); // Flush (send) the output buffer and turn off output buffering ob_end_clean(); ob_start(); header("Content-Length: $size"); echo $soapXml; ob_end_flush(); // Unless both are called ! flush(); // Continue do another process after sent message //example sleep(10); fwrite($fp, "Test Writing\n"); fclose($fp);
?>
soapclient.php:
<?php // client $options = array( 'location' => 'http://test.local/stack/soapserver.php', 'uri' => 'http://test.local/stack/soapserver.php' ); $client = new SoapClient(NULL, $options); echo $client->addNumbers(3, 5); // 8
You'll immediately see response 8
in broswer. After 10s, you'll see Test Writing
in file data.txt
0 comments:
Post a Comment