One of the great things about using PHP is the sheer number of built in functions (BIFs). Unfortunately though, some of the functions are not documented that well. Take the SoapClient class for example. I just spent a few hours today trying to configure it, so I'm posting up my experience so hopefully others don't have to spent those hours.
The problem:
I am trying to connect to a web service for a third party, and here is the bit of the wsdl that is interesting:
<wsdl:definitions targetNamespace="http://questionmark.com/QMWISe/">
...
<s:element name="Security" type="tns:SecurityHeader"/><s:complexType name="SecurityHeader"><s:sequence><s:element minOccurs="0" maxOccurs="1" name="ClientID" type="s:string"/><s:element minOccurs="0" maxOccurs="1" name="Checksum" type="s:string"/></s:sequence><s:anyAttribute/></s:complexType>
...
</wsdl>
When I make a call, I have to construct the header according to this:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:qmw="http://questionmark.com/QMWISe/">
<soap:Header>
<qmw:Security>
<!--Optional:-->
<qmw:ClientID>id</qmw:ClientID>
<!--Optional:-->
<qmw:Checksum>hash</qmw:Checksum>
</qmw:Security>
</soap:Header>
<soap:Body>
...
</soap:Body>
</soap:Envelope>
Simply enough right? Well not with the way SoapClient is structured.
This was what I had initially:
In client code:
$this->client = new SoapClient(null, $this->constructParams());
$this->client->__setSoapHeaders(new SoapHeader($this->targetNamespace, "Security",
new SoapVar(new Security($this->username, $this->getMd5Hash()), SOAP_ENC_OBJECT));
In class Security:
class Security {
function Security($clientId, $md5Hash) {
$this->ClientID = $clientId;
$this->Checksum = $md5Hash;
}
}
Since we're using the SOAP_ENC_OBJECT flag which will tell PHP to use the variable name of the Security object, everything should be good to go right? Wrong. This is what it spits out:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://questionmark.com/QMWISe/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:enc="http://www.w3.org/2003/05/soap-encoding">
<env:Header>
<ns1:Security>
<ClientID>id</ClientID>
<Checksum>hash</Checksum>
</ns1:Security>
</env:Header>
<env:Body>
...
</env:Body>
</env:Envelope>
Notice the difference? It's missing the namespace declaration for <ClientID> and <Checksum>, which leads to web service server complaining when we send out the request.
The solution:
So rather than using the docs from PHP, this is what I did to get around the issue:
new SoapHeader($this->targetNamespace, "Security",
new SoapVar(array("ns1:ClientID" => $this->username, "ns1:Checksum" => $this->getMd5Hash()), SOAP_ENC_OBJECT));
The only issue here is that we are coding the ns1 namespace in the parameters. Although the namespace prefix won't change it just annoys me that I have to introduce this workaround in there to fix the issue. This is a workaround because it appears there is a PHP bug filed for this misbehavior:
When we do that, we see the correct behavior:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://questionmark.com/QMWISe/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:enc="http://www.w3.org/2003/05/soap-encoding">
<env:Header>
<ns1:Security>
<ns1:ClientID>id</ns1:ClientID>
<ns1:Checksum>hash</ns1:Checksum>
</ns1:Security>
</env:Header>
<env:Body>
...
</env:Body>
</env:Envelope>
No comments:
Post a Comment