How to attach files to soap envelopes using DIME and PHP
TweetIntroduction
As you may be aware by now, PHP native SOAP implementation does not support DIME transfers (this implies that the one from Zend Framework(tm) does not support them either). Also, the PEAR soap implementation lacks enough documentation to make it work (at least, I couldn't from the client side ;))
In this article I'll show you my own solution, which is a little dirty but does the job. What I did is to use PEAR Net_Dime to create the message, and then post it via curl. Obviously this can be refined a lot, but it may help you in dire, time-lacking situations ;)
Example
So let's say you want to send a soap request with cero or more attachments, using DIME as the transport (I had this requirement when dealing with WoodWing(tm)). You will first need to get the text of the soap envelope request, like:
$req = "<?xml version='1.0' encoding='UTF-8' ?> <SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/' xmlns:ns1='urn:Something'> <SOAP-ENV:Body> <ns1:SomeMethodRequiringAnAttachment> </ns1:SomeMethodRequiringAnAttachment> </SOAP-ENV:Body> </SOAP-ENV:Envelope>";
The plan, is to send this message using curl, with the following code:
/** * Send a POST requst using cURL * @param string $url to request * @param array $post values to send * @param array $options for cURL * @return string */ function curl_post($url, $post = NULL) { $defaults = array( CURLOPT_POST => 1, CURLOPT_HEADER => 0, CURLOPT_URL => $url, CURLOPT_HTTPHEADER => array( 'Content-Disposition: form-data; name=\"soap\"; filename=\"123\"', 'Content-Type: application/dime' ), CURLOPT_FRESH_CONNECT => 1, CURLOPT_RETURNTRANSFER => 1, CURLOPT_FORBID_REUSE => 1, CURLOPT_TIMEOUT => 400, CURLOPT_POSTFIELDS => $post, CURLOPT_BINARYTRANSFER => true ); $ch = curl_init(); curl_setopt_array($ch, $defaults); $result = curl_exec($ch); if(!$result) { trigger_error(curl_error($ch)); } curl_close($ch); return $result; }
And this is the code that put the pieces together and actually sends the message with the attachments:
function send($url, $msg, $attachments = array()) { $tempFile = tmpfile(); if ($tempFile === false) { throw new \Exception('Could not create temp file'); } $dime = new Net_DIME_Message($tempFile); $dime->sendData($msg, 'http://schemas.xmlsoap.org/soap/envelope/'); foreach ($attachments as $id => $attachment) { $dime->sendData($attachment, 'application/octet-stream', $id); } rewind($tempFile); $msg = stream_get_contents($tempFile); fclose($tempFile); $ret = curl_post($url, strlen($msg), '', $msg); return new SimpleXMLElement($ret); }
Using it
Now, how to use it:
$file = './a.jpg'; $fileContents = file_get_contents($file); // This is computed so the attachment id is the md5 sum of the file in question. $fileMD5 = md5($fileContents); $result = send($req, array($fileMD5 => $fileContents));
After running the request, and if everything went ok, you will have a SimpleXMLElement with the soap response. The request consists of a DIME message with the first attachment being the soap request itself, and the following are the attachment themselves.
Conclusions
Quick, dirty, a little low-level, but effective. Lot to improve, but that's the subject of another article ;) This will let you quickly implement and/or test your dime client. Let's hope that php gets a little better soap support sometime.