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>
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>";
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With