4

I'm writing a PHP application which uses a number of SOAP web services to gather data.

I'm getting significant overheads in instantiating all those objects: in some cases a single line of code $object = new SoapClient($wsdl); can take over three seconds. It obviously only takes a few of those to make a web page feel really slow.

To speed things up a bit, I figured I'd serialise the objects and store them in the session (or somewhere similar), so I wrote the following function:

function soap_client($name,$wsdl) {
    if (!isset($_SESSION['soapobjects'][$name])) {
        $client = new SoapClient($wsdl, array('trace' => 1));
            $_SESSION['soapobjects'][$name]=serialize($client);
        } else {
            $client = unserialize($_SESSION['soapobjects'][$name]);
        }
    return $client;
}

That certainly seems to be the way PHP recommends to do it.

...and then calling it like this...

$client = soap_client('servicename',$wsdl);
$client->MethodName($parameters);

However, it doesn't seem to work.

The first time you run it, it works (ie the object is created and a serialised copy is made, and the method call works fine). However the second time you run it, it fails.

The object appears to serialise and de-serialise correctly, but when you try to execute a SOAP call on the de-serialised object, it throws the following error:

Fatal error: Uncaught SoapFault exception: [Client] Error finding "uri" property

Clearly the de-serialised object is not the same as the original object, which is at odds with how object serialisation is supposed to work.

Can anyone explain why I'm getting this error? Can you suggest a way to get it working, or an alternative strategy that I could be persuing?

Thank you.

ps - I've tried to work around the problem, but no joy.

I've tried specifying the URI in the options parameter (as specified in the PHP SOAP Client manual), but it didn't make any difference. But it shouldn't be necessary anyway, as I'm using a WSDL.

I've also tried simply copying the object to $_SESSION, without using serialize() and deserialize(), but that has exactly the same effect.

hakre
  • 193,403
  • 52
  • 435
  • 836
Spudley
  • 166,037
  • 39
  • 233
  • 307

2 Answers2

3

The built-in SOAP extension is an undebuggable binary blob of horrors. It's very likely that it was not built with serialization in mind. For example, it could contain an internal filehandle that won't survive the serialize/unserialize process. I urge you to use some other SOAP client, such as:

  • Zend_Soap, part of the Zend Framework. You don't need to use the Framework in any other area of your code, and can probably remove most of the other components. However, it seems to use the existing SOAP extension under the covers, so it's probably not a good candidate for serialization.
  • PEAR's SOAP is frequently referenced, though it's a bit old.
  • NuSOAP has recently been brought back from the dead, though all of the online documentation appears to have disappeared into zip files.

If none of these are suitable, consider caching the WSDL file locally, as I somehow expect that's where the delay is.

Charles
  • 50,943
  • 13
  • 104
  • 142
  • Thanks for the reply. I've done some further investigation, and it looks like your analysis is spot on -- the serialised string is virtually useless for rebuilding the object; it's got virtually nothing in it, so it's no surprise that deserialising it doesn't work. I'll look into the alternatives, but PHP SOAP is quite well baked into our app, so it may be too much pain to switch. We already looked into WSDL caching; it hasn't worked thus far, but I'll re-visit it, as I don't think we got it right before. – Spudley Mar 24 '11 at 11:05
  • Instead of learning another soap extension, i would recommend looking into wsdl caching. Unless of course there is a specific feature you are after. – denormalizer May 31 '11 at 07:17
  • 2
    @Charles Using Zend_Soap_Client doesn't work neither because it wraps an [extended SoapClient](http://framework.zend.com/apidoc/1.11/db_Soap_Client_Common.html#%5CZend_Soap_Client_Common) class. Storing this into a (Zend_) cache or a (Zend_) session leads to deserializing issues also unless you haven't used the client before. Because of Zend_Soap_Client lazy loading the underlying SoapClient this could lead to unforseen side effects in an application. – Partyschaum Feb 03 '12 at 11:33
  • Thanks, @Partyschaum, I've integrated that information into my answer. – Charles Feb 03 '12 at 16:09
2

As per your comments to the previous answer, the best option would be to use Zend framework only for soap as suggested before, this will allow you to keep using php and still have a better functionality with zend. Also you can utilize other features of Zend incase your requirements increases.

This example might help you http://blog.fedecarg.com/2009/02/15/building-a-web-service-client-using-the-zend-framework/

pal4life
  • 46
  • 2