Selecting the service port with PHP's SoapClient
I’m currently integrating with a SOAP service which has two different services defined.
The relevant part of the WSDL is:
<wsdl:service name="Config"> <wsdl:port name="BasicHttpBinding_IConfiguration" binding="tns:BasicHttpBinding_IConfiguration"> <soap:address location="http://nonsecure.example.com/Configuration.svc"/> </wsdl:port> <wsdl:port name="BasicHttpsBinding_IConfiguration" binding="tns:BasicHttpsBinding_IConfiguration"> <soap:address location="https://secure.example.com/Configuration.svc"/> </wsdl:port> </wsdl:service>
I discovered that PHP’s SoapClient will select the first port it encounters and doesn’t provide a way to select another one. This was a nuisance as I wanted to use the SSL one.
Through research, I discovered that I can use __setLocation():
$client->__setLocation('https://secure.example.com/Configuration.svc');
However, I don’t control that endpoint, so I would rather select based on the port’s name.
As I couldn’t find a way to get the data from SoapClient, I decided to parse the WSDL myself and pull the information out. I don’t do a lot with XML namespaces, so had to look up how to handle them and then how to extract the right data using XPath.
As I had to look it up, I’m putting it here, so I can find it again more easily!
I converted my new found knowledge into a method to extract the location attribute from the <soap:address> element of the <wsdl:port> with the correct name element:
function getLocationForPort($wsdl, $portName) { $file = file_get_contents($wsdl); $xml = new SimpleXmlElement($file); $query = "wsdl:service/wsdl:port[@name='$portName']/soap:address"; $address = $xml->xpath($query); if (!empty($address)) { $location = (string)$address[0]['location']; return $location; } return false; }
Xpath is ideal for this job!
Usage is simply:
$client = new SoapClient($wsdl); $sslLocation = getLocationForPort($wsdl, 'BasicHttpsBinding_IConfiguration'); if ($sslLocation) { $client->__setLocation($location); } // work with $client as normal
Now, all my calls to the SOAP service are via SSL as they should be!
Hi Rob,
In my case "wsdl:service/wsdl:port[@name='$portName']/soap:address" did not work. Instead I used: "wsdl:service/wsdl:port[@name='$portName']/child::*". Also this:
if ($sslLocation) {
$client->__setLocation($location);
}
Should be:
if ($sslLocation) {
$client->__setLocation($sslLocation);
}
Cheers,
Ehsan