When delving into the magical world of trying to get a PHP SoapClient to consume a Java JAX-WS web service, there may be a few unexpected surprises that pop up. If you have been using SoapClient to communicate with other PHP SoapServer service, things are pretty straightforward. However, things get slightly more complicated when trying to call Java based JAX-WS services. Luckily, there are some simple solutions to make it a breeze to get these two to communicate seamlessly.
The Request Problem
When you first try to consume a Java JAX-WS web service, chances are that you try to call a method on that service using something like the following:
-
-
$client = new SoapClient("http://some-service-url:8080/MyService?wsdl");
-
$sum = $client->add(1, 2);
As you soon discover, this throws a SoapFault error such as the following:
-
Fatal error: Uncaught SoapFault exception: [S:Server] java.lang.NullPointerException ….. etc…
Basically the problem is that JAX-WS is never getting the method parameters that we are trying to send over…
The Solution
Unlike calling another PHP SoapServer web service which will work flawlessly with unnamed parameters, JAX-WS is expecting just a little more. If we take a look at the WSDL for this service method, we see the following:
-
<message name="add">
-
<part name="parameters" element="tns:add"/>
-
</message>
-
<xs:complexType name="add">
-
<xs:sequence>
-
<xs:element name="a" type="xs:int" minOccurs="0"/>
-
<xs:element name="b" type="xs:int" minOccurs="0"/>
-
</xs:sequence>
-
</xs:complexType>
So, what we see here is basically that the add() method is expecting an "add" request object. What we need to do is wrap our parameters into a request object so that JAX-WS will know how those parameters are mapped to the parameters it is expecting. This can easily be achieved by using PHP’s handy stdClass. So now our code becomes:
-
-
$addRequest = new stdClass();
-
$addRequest->a = 1;
-
$addRequest->b = 2;
-
$response = $client->add($addRequest);
However, we can simplify this even more by using an associative array for the parameters. SoapClient will automatically know to translate an associative array into a complex type for the web service. So now we can do this:
-
-
$sum = $client(array('a' => 1, 'b' => 2));
Sweet! That’s much simpler, plus now it’s clear what each of the parameters are!
The Response Problem
Soon you will discover that this web service is not returning what you expected. Looking at the example, you would expect that the service simply returns an integer with the value 3. Sadly, it’s not that simple. The JAX-WS services will always pass back a response object for each method. By examining the response in the WSDL, we can see exactly what is happening:
-
-
<xs:complexType name="addResponse">
-
<xs:sequence>
-
<xs:element name="return" type="xs:int" minOccurs="0"/>
-
</xs:sequence>
-
</xs:complexType>
So, the add() method is returning an addResponse object. Since we have not defined any class mapping for our SoapClient, this response simply gets returned as a stdClass object with a single property named "return". With this knowledge, we can now see that we need to access the response like this:
-
-
$addResponse = $client->add($addRequest);
-
$sum = $addResponse->result;
The Ultimate Response Solution
While the response example above shows a solution to getting the correct response, it isn’t necessarily the most elegant. When calling a service method and getting the result, it would be nice to automatically have the value you are expecting instead of having to add one more step of grabbing the final result from an object.
Luckily this can be easily achieved by extending the SoapClient class and tinkering it just a little bit. We are going to simply extend the class, overload the __call magic method, and do our response parsing in there.
So, here we create our new JAX-WS specific SoapClient class:
-
-
class JaxWsSoapClient extends SoapClient
-
{
-
public function __call($method, $arguments){
-
$response = parent::__call($method, $arguments);
-
return $response->return;
-
}
-
}
Now, we can do this:
-
-
$client = new JaxWsSoapClient("http://some-service-url:8080/MyService?wsdl");
-
$sum = $client->add(array('a' => 1, 'b' => 2));