Writing SOAP Web Service Consumers in PHP

Recently I’ve had to interact with a number of SOAP web services, and I’ve come up with some techniques to help build good SOAP web service consumers.

The two most important goals when building a SOAP consumer is for the code to be maintainable and testable.

Dark Beginnings

A natural first approach is to write one class that handles everything necessary to interact with the web service:

What we’ve written above is difficult to test because we’re constructing the SoapClient ourselves. Testing any methods on the consumer would mean making a real call to the web service.

We can do better:

By having the consumer declare outright what it requires to work (an instance of a client) we’re now able to construct the consumer with mock instances of the SoapClient where we can fake real calls to the web service.

Bonus: the consumer is no longer responsible for constructing a SoapClient object with the correct credentials. It’s always a good sign to reduce a class’s responsibilities.

Writing a Test

Now that we have a way to inject a client into our consumer, let’s use Mockery to fake a web service call:

Since PHP’s SoapClient returns a SimpleXml  object from a web service method call, that’s what we’ll have our mock object return too – except we’ll use our predefined XML snippet in order to control our testing environment.

We’re now free to test the response as we sit fit!

Avoiding a 4000 Line Long Consumer Class

As you write more and more code to consume the methods of the web service, your class will quickly grow long. This will happen especially quickly if there is any complex logic associated with formatting web service method requests and responses.

The approach I use to keep classes short is to write one class per web service method. Here’s how:

The refactored consumer class now looks for a class in the App\Methods\* namespace with the same name as the method being called. If found, it will create an instance of the class, and delegate to it.

In our example, a call to $consumer->getBooks() would internally be routed to another class called App\Methods\GetBooks .

No matter how many methods we need to consume, our consumer class will never get any bigger!

Bonus: our consumer class now conforms to the Open and Closed principle.

Here’s what our App\Methods\GetBooks class looks like:

Caching Calls

A common optimization technique is to cache web service calls. With a small tweak to our consumer, we can allow our method classes to be responsible for their own caching:

Whenever a method class implements the App\Cacheable interface, the cache method will be called instead. If using Laravel, this could look like the below:

Logging Calls

Debugging will be infinitely easier if all your web service calls are logged. Since we’re injecting the SoapClient into our consumer, we can write a simple decorator to log all web service calls:

For the request to be logged correctly, we’ll need to enable tracing when configuring our SoapClient:

Of course, we can configure Laravel’s Container to correctly build our consumer class whenever we request it, by writing the following code in a service provider:

End

Dealing with SOAP web services can be a messy business. Use and improve upon the techniques written here to make the process a little bit more pleasant.

Happy coding!

Share: Share on FacebookTweet about this on TwitterShare on LinkedIn
  • Orihime

    Do you have a repository to see the complete SOAP Web Service example?