Make your own Soap API

Our PHP expert, Paul Hudson, walks you through how to create your own Soap API for others to use

I’m sure you’ve all noticed that this magazine enjoyed a comprehensive redesign a few issues ago, and we adopted the new slogan ‘develop/ discover/design’. Well, I’m a man with so little sense of appearance that I dress in the dark, so I can’t teach you anything about design. But in the last two issues, I hope you’ve joined me to discover how to take advantage of the excellent Google, Fl ickr and Delicious APIs to get extra content for your site.

This issue, we’re going to look at my favourite member of the .net trinity: ‘Develop’. When working with Google, we had to use Soap and WSDL, two of the simplest methods of defining a strict API for others to build on. We already used the excellent SoapClient class, but there’s an equally excellent SoapServer class that also works with WSDL, and we can combine them to share our content with the world, all thanks to PHP. Of course, our code will serve up content to everyone who requests it regardless of whether or not they’re using PHP, but our server will be written using PHP.

The advantage of using WSDL is that we define our API in a clear, crossplatform way using an open XML standard, rather than trying to write a few thousand lines of PHP then have to write all the documentation to go along with it. WSDL provides the basis for our code, but is also self-documenting, which is perfect for anyone who doesn’t enjoy writing documentation (that’s all of us). The fact that our WSDL provides the base for our client and server code is an added bonus that means you only need to change one file to keep your API up to date.

Working out the WSDL

The very first step in creating an API is to decide what you actually want to achieve. This is usually done through use case diagrams, which are designed to compile a list of required inputs and outputs for a process. I don’t have the space within these pages to open a software engineering school, so I’m going to keep the requirements quite simple: I’m going to serve up puzzles. To do this, the API will define a getPuzzle() function that sends a request off to the server, which then picks a puzzle at random and sends it back. To make things a little more difficult, you’re going to let people specify what level of difficulty they want, with level 1 being the easiest and level 3 being the hardest.

Now, a word of warning: when working with WSDL, creating the first working code is always the most difficult. Your basic WSDL script is just shy of 40 lines of XML, and understanding it isn’t as easy as you may think. The actual PHP to drive the whole affair is a cinch in comparison, but you need to write the WSDL first.

To make it more readable, it’s good practice to break the WSDL into two parts. Save the whole file as ‘dotnet1.wsdl’ and make sure it’s in your public HTML directory. Here’s the first part:

<?xml version="1.0"?><definitions name="NetPuzzle" targetNamespace="urn:NetPuzzle" xmlns:typens="urn:NetPuzzle" xmlns:xsd="" xmlns:soap="" xmlns:soapenc="" xmlns:wsdl="""xmlns=""><message name="getPuzzle"><part name="difficulty" type="xsd:int" /></message><message name="getPuzzleResponse"><part name="return" type="string" /></message><portType name="NetPuzzlePort"><operation name="getPuzzle"><input message="typens:getPuzzle" /><output message="typens:getPuzzleResponse" /></operation></portType>

The opening two lines are standard XML fare, though you’ll notice that you need to suck in a number of XML namespaces, as a variety of technologies are being used. The next two sections define the ‘messages’ and ‘operations’ we wish to perform. Each message is either something that’s sent to the server or something received back, and it’s here where you define exactly what the messages should contain. For your basic needs, I’ve said that the getPuzzle message must contain a number (‘int’ is short for ‘integer’, meaning ‘whole number’) that is the difficult rating, and that the puzzle response should be in the form of a string.

Beneath the messages is the portType element, but don’t worry about this just yet. For now, care only about its contents, which state that there’s an operation (a function, in PHP terminology) called getPuzzle(), which is called through the getPuzzle() message and returns a getPuzzleResponse() message. It’s operations that tie messages together into functions, which is why the name of the operation, getPuzzle, gets mapped to the getPuzzle() PHP function. Here’s the other half of the WSDL file:

<binding name="NetPuzzleBinding" type="typens:NetPuzzlePort"><soap:binding style="rpc" transport="" /><operation name="getPuzzle"><soap:operation soapAction="urn:NetPuzzleAction" /><input><soap:body use="encoded" namespace="urn:NetPuzzle"encodingStyle="" /></input><output><soap:body use="encoded" namespace="urn:NetPuzzle"encodingStyle="" /></output></operation></binding><service name="NetPuzzleService"><port name="NetPuzzlePort" binding="typens:NetPuzzleBinding"><soap:address location="" /></port></service>

The whole <binding> element is required, yet can largely be ignored. What it does is say that the getPuzzle operation uses Soap for both its input and output, but in true XML style, it takes more than 10 lines of code to say this. If you want to add your own operations in future, just copy and paste this wholesale. You won’t actually need to change anything other than the name of the operation being used.

The final element, <service>, is very important, as it defines where the service resides on the internet. The portType element mentioned earlier references this before describing the operations that are available. In practice, this means you can have different operations pointing to different scripts if you want to.

You’ve now defined your parameters (or ‘messages’, in Soap-speak), your function (‘operation’), your protocol (‘binding’) and your URL (‘service’), which means you have all you need to make a complete function call. This means you can leave XML behind for now and switch to PHP.

Client and server

After so much XML, this code will probably make you laugh:

<?php$soap = new SoapClient("dotnet1.wsdl");echo $soap->getPuzzle("1");?>

Two lines of PHP is all it takes to load the API, make the function call and print the results. Save this as ‘1.php’ in the same directory as your WSDL file. This code requests an easy puzzle from the server, so you need to program the final component of the API: the server. This needs to accept the Soap request, handle the getPuzzle() function, then output the puzzle for the client. PHP does most of the hard work for you, which makes the code remarkably short:

<?phpfunction getPuzzle($difficulty) {$puzzles[1][] = "What is the best web mag in the world?";$puzzles[2][] = "What is the air-speed velocity of an unladen swallow?";$puzzles[3][] = "What is the meaning of life?";$randpuz = array_rand($puzzles[$difficulty]);return $puzzles[$difficulty][$randpuz];}$server = new SoapServer("dotnet1.wsdl");$server->addFunction("getPuzzle");$server->handle();?>

The bulk of this script is the getPuzzle() function, which takes a parameter to store the difficulty that was requested. This function stores your puzzles in the $puzzles array, but if you want to implement this seriously, you should switch to an SQL database with a query similar to this one:

SELECT Question FROM puzzles WHERE Difficulty = $difficulty ORDER BY rand()

The function returns a value by plucking a question randomly from the $puzzles array at the correct difficulty level. Of course, each difficulty level has just one question, so the same question is always returned! The three lines at the end of the script comprise the Soap magic that hooks up our PHP function to the Soap getPuzzle() call. You need to call addFunction() for each Soap/PHP bridge you want to register. When you’re done, you simply need to call the handle() method to have PHP parse the Soap input, route it to the appropriate functions, then send the value back to the client.

I know you’re probably eager to run the code, but you first need to disable PHP’s WSDL cache. You’ve already seen that the WSDL required to make a single function call is very long, so to avoid having to parse and validate it every single time a client comes along, PHP caches the WSDL in its parsed state. While you’re still at the development stage, this causes problems. Any changes made to the WSDL file won’t be spotted by PHP, because it will still be using its cached version. The solution is to temporarily disable PHP’s caching system, which can be achieved by changing your ‘php.ini’ file: look for the ‘soap.wsdl_cache_enabled’ line and change it to ‘0’.

With this change implemented (restart Apache if you’re running PHP as a module), load your ‘1.php’ script. You should see ‘What is the best web mag in the world?’, which shows that our client has received a message back from the server.


Before we finish, there are three final points I’d like to make to help you keep on top of your WSDL. First, ensure your API is stable. If people get used to calling your getPuzzle() method, then don’t change it. Don’t add new parameters, don’t change the data type of existing parameters and certainly don’t drastically alter what the function actually does. That doesn’t mean you can’t rewrite your PHP code to have getPuzzle() do the same thing in a smarter way, but if you really must change the API (ie, add a parameter or whatever), leave the old one alone and make a new one called getPuzzle2(). Second, don’t forget to notify people when your WSDL file changes, so that they know to download the newest version to take advantage of the newest features. Finally, although PHP doesn’t distinguish between ints and strings, other languages do, so choose them wisely.

Hopefully, you’ve seen that – at least once you’re past the initial pain of writing WSDL – using Soap is easy. PHP takes away so much of the pain of handling data transfer, which means you can focus on getting your functions correct on the client and server sides. As you progress more with WSDL, you’ll learn to appreciate its verbosity, because PHP is able to do some basic type-checking on your data to ensure you’re sending the right content through Soap.