Simple PHP SOAP Client
Recently I was asked to verify email addresses against a client’s SOAP (Simple Object Access Protocol) server to verify data. I have worked with and created RESTful APIs but haven’t really had much experience with creating a PHP SOAP client. Here is what I came up with.
What is SOAP?
SOAP is a messaging protocol that allows programs that run on disparate operating systems (such as Windows and Linux) to communicate using Hypertext Transfer Protocol (HTTP) and its Extensible Markup Language (XML).
Basically, SOAP is a protocol that you use by sending an XML formatted string and receiving an XML response. SOAP v 1.2 was released in 2007 and since then we have moved on to RESTful JSON APIs. However, some legacy systems still require communication with SOAP.
What information do I need?
We were given a document of example requests and responses. The key things that I wanted to find were the End Point and the Request we need to send. The client has already given us the server End Point (http://www.example.org/getInfo.asmx). Here is the example SOAP request.
<?xml version="1.0"?> <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope/" soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding"> <soap:Body xmlns:m="http://www.example.org/stock"> <m:GetStockPrice> <m:StockName>IBM</m:StockName> </m:GetStockPrice> </soap:Body> </soap:Envelope>
In this XML we are sending a GetStockPrice request and it requires a value for the namespace StockName.
PHP Code
To start out I want to organize this in a way so the XML is in its own file as a variable. In a file called xmldata.php place your XML request.
<?php $xmlData = '<?xml version="1.0"?> <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope/" soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding"> <soap:Body xmlns:m="http://www.example.org/stock"> <m:GetStockPrice> <m:StockName>IBM</m:StockName> </m:GetStockPrice> </soap:Body> </soap:Envelope>'; ?>
Note that there is no space at the beginning of the XML. The server will bark at you if you put a space in the beginning.
Next, create your main PHP file, I called mine soaprequest.php
<?php include ('xmldata.php'); $url = 'http://www.example.org/getInfo.asmx?WSDL'; $ch = curl_init($url); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml')); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, "$xmlData"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $output = curl_exec($ch); curl_close($ch); $response = preg_replace("/(<\/?)(\w+):([^>]*>)/", "$1$2$3", $output); $xml = new SimpleXMLElement($response); $body = $xml->xpath('//soapBody')[0]; $array = json_decode(json_encode((array)$body), TRUE); print_r($array); ?>
So, let’s break this down line by line.
include ('xmldata.php'); $url = 'http://www.example.org/getInfo.asmx?WSDL';
The include is letting us reference $xmlData that is set in xmldata.php. The next line is setting $url to the End Point.
$ch = curl_init($url); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml')); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, "$xmlData"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $output = curl_exec($ch); curl_close($ch);
This is the heart of the procedure. We are using cURL to make this request. In line order, this is what is happening:
- curl_init creates a new cURL resource for us to use.
- SOAP header is set to Content-Type: text/xml.
- CURLOPT_POST is set to true, therefore we are calling for a standard HTTP POST.
- CURLOPT_POSTFIELDS is the full data to send in the HTTP POST. In this case, our formatted XML.
- Setting CURLOPT_RETURNTRANSFER variable to 1 will force cURL not to print out the results of its query. Instead, it will return the results as a string return value from curl_exec() instead of the usual true/false.
- The variable $output is set to the response of the cURL execution.
- Close cURL resource, and free up system resources
$response = preg_replace("/(<\/?)(\w+):([^>]*>)/", "$1$2$3", $output); $xml = new SimpleXMLElement($response); $body = $xml->xpath('//SoapBody')[0]; $array = json_decode(json_encode((array)$body), TRUE);
- We remove any “:” that separate the XML namespaces.
- $xml is created as a new SimpleXML element from $response.
- The XML structure is navigated and captured at the “SoapBody” (which was “Soap:Body” before we removed the “:”) and set to $body.
- $array is set as a jSON encoded version of the XML. The json_decode(json_encode(array)$body) trick changes the json encoded data from an object to an array.
Finally, we have an array with our response in it which you can print_r or var_dump from there to see your results.
Now, this isn’t very dynamic, we would have to hard code the Stock name every time. How about we take in a variable and replace it in the XML instead? That would look something like this:
soaprequest.php
<?php include ('xmldata.php'); $url = 'https://lab6.guestwarehost.com/guestware/gwwebgst.asmx?WSDL'; $stockName = 'IBM'; $replace = '###STOCKNAME###'; $xml_data = str_replace($replace, $email, $xmlData); $ch = curl_init($url); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml')); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, "$xmlData"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $output = curl_exec($ch); curl_close($ch); $response = preg_replace("/(<\/?)(\w+):([^>]*>)/", "$1$2$3", $output); $xml = new SimpleXMLElement($response); $body = $xml->xpath('//soapBody')[0]; $array = json_decode(json_encode((array)$body), TRUE); print_r($array); ?>
xmldata.php
<?php $xmlData = '<?xml version="1.0"?> <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope/" soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding"> <soap:Body xmlns:m="http://www.example.org/stock"> <m:GetStockPrice> <m:StockName>###STOCKNAME###</m:StockName> </m:GetStockPrice> </soap:Body> </soap:Envelope>'; ?>
Here a variable $stockName replaces the placeholder “###STOCKNAME###” in the XML. This can come from a DB, $_GET,$_POST or wherever.
Tips:
Postman: I used Postman to test my request many times before I even started coding PHP. I wanted to make sure that my connection, request and returned data all came back as expected. This just made my debugging of code easier.
Command Line: This is just a simple command line script for ease of testing. This would be super simple to put into a web server and check for $_POST or $_GET for the input.
Thanks, good tuto