Since PHP5, doing stuff with Soap became way easier. SoapServer and SoapClient where added, giving pretty much everybody the ability to create a simple SOAP provider and consumer. But, as always, there is a but. And the ‘but’ in this case, is that if you go to server.php?wsdl, you get an error stating “WSDL generation isn’t supported.” And anybody who has ever written a WSDL by hand knows that it sucks. So, my mission of tonight: to generate a WSDL file from my PHP code.
Obviously, I started with a search for something like “php generate wsdl from code”. That exact search gives my over 2m results on Google. To bad non of them had the answer for me. Then, I read somebody directing somebody else in the direction of Zend_Soap_AutoDiscover and decided to take a look myself. It seemed to do what I wanted it to, so I figured, why not have a look at it.
After downloading ZendFramework to my laptop and setting up autoloading for it, the first thing I tried was creating a script separate from my server.php called generate.php. In that script, I included my service class, told Zend_Soap_AutoDiscover I wanted to use that and to handle() requests. Browsed to the file, and voila, I had my WSDL file. So, I saved the file. Then, I found out Zend_Soap_AutoDiscover has a dump($filename) method that can do that for me. Instructed SoapServer and SoapClient to use that WSDL, and it worked!
Well, somewhat. Calling $client->__getFunctions() showed me my functions. But if I tried to call one of them, it threw an exception. After some working, I noticed that the targetNamespace was http://localhost/soap/generate.php, which is plain wrong as I’m calling http://localhost/soap/server.php. Ah, that makes sence – Zend_Soap_AutoDiscover couldn’t have known I wanted to use server.php now could it?
So then I thought, why not put the Zend_Soap_AutoDiscover code in my server.php, detect ?wsdl and give the WSDL in those cases. After first having a small codesnippet in server.php that detects ?wsdl and lets Zend_Soap_AutoDiscover do its thing -which worked- I decided the final step was to extend SoapServer so that it would, in fact, support WSDL generation. (Sure, not native, but for native support it’d need to be done in C, which I don’t know anywhere near well enough to make an appempt.)
So I went ahead and wrote my class, WsdlGeneratingSoapServer (describing names for the win). It didn’t need to do much – just overwrite setClass, setObject and addFunction so that it can keep track of what the SoapServer can do (and then passing on the call to parent::, obviously) and overwrite handle, which should detect ?wsdl and, if found, have Zend_Soap_AutoDiscover do the generating, calling parent::handle otherwise.
Sounds easy enough. And it was, I had the class done in about 10 minutes. And then, I tested it. WSDL generation worked perfectly, so goal accomplished! Or well, not really. Normal soapcalls now result in a SoapFault. So I started to try changing things. Long story short, this simple code caused a soapfault. And since thats crazy, copied that script from my laptop (which has PHP Version 5.3.2-1ubuntu4.2 + Suhosin Patch 0.9.9.1) to my VPS (which has PHP Version 5.3.3RC4-dev) and what do you know, it worked.
I didn’t feel like doing any more PHP tonight, so decided to write this blogpost instead. I’ll try the code at work tomorrow, hopefully it’ll work there as well. 🙂
In the mean time, here is the code. If you change new SoapServer(…); to new WsdlGeneratingSoapServer(…); (and have Zend_Soap_AutoDiscover, which is part of Zend Framework), you’ll have WSDL generating abillities, too!